@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,344 @@
|
|
|
1
|
+
# board — one shared feature board per repository
|
|
2
|
+
|
|
3
|
+
Every Pi session on a repository reads and writes the same cards, and a repository
|
|
4
|
+
has **one** page: the session that starts it serves it under the seat `team-lead`, and
|
|
5
|
+
every other session opens that page read-only instead of a second one. A card is one
|
|
6
|
+
**feature**: its outcome, who holds it, where the work happens, the plan it is
|
|
7
|
+
executing, how urgent it is, what it waits for, and the notes that explain how it got
|
|
8
|
+
there.
|
|
9
|
+
|
|
10
|
+
The steps of the work are deliberately *not* on the board. They live in the
|
|
11
|
+
owner's plan and in `todo`; the board answers who is doing what, and what needs a
|
|
12
|
+
decision.
|
|
13
|
+
|
|
14
|
+
<img src="../../assets/screens/board.png" alt="The board page: a toolbar with the seat the page writes as, a live badge, Board and Team tabs, done-of-total progress, a filter and a New card button; four lane trays, each with a dot, a count and a fold control; and cards carrying an owner chip, path hints, a reason line and their actions" width="100%">
|
|
15
|
+
|
|
16
|
+
*`/board`: the four lanes, with a new card one keystroke from its dialog and the round one tab away.*
|
|
17
|
+
|
|
18
|
+
## Capabilities
|
|
19
|
+
|
|
20
|
+
| Capability | How | What you get |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| A shared feature list for one repository | `board` action `list` | Every card in lane order — `todo`, `doing`, `blocked`, `done` — or one card, or one lane |
|
|
23
|
+
| The lead's rollup | `board` action `list` with `round: true` | Every owner with its live cards and, beside the count, what it has finished — an owner whose last card is done stays listed — then unassigned, unplanned, blocked, idle, and done |
|
|
24
|
+
| Publish a feature | `board` action `add` | A `todo` card with path hints and a first note |
|
|
25
|
+
| Take a feature | `board` action `claim` | The card is `doing` and owned by this session |
|
|
26
|
+
| Hand it back | `board` action `release` | The card is `todo` again, with no owner |
|
|
27
|
+
| Change lane | `board` action `move` | Wherever the owner moves it: `doing`, `blocked`, `done` |
|
|
28
|
+
| Record what happened | `board` action `note` | A note appended to the card's history, attributed and stamped |
|
|
29
|
+
| Record the plan | `board` action `plan` | The plan file the card is executing, so "unplanned" work is visible |
|
|
30
|
+
| Fix what a card says | `board` action `edit` | A new title, path hints, priority, or the cards it waits for — by its holder, or by anyone while nobody holds it |
|
|
31
|
+
| Say how urgent it is | `board` action `add` or `edit` with `priority` | `high` sorts first inside its lane, and the card says so |
|
|
32
|
+
| Record what a card waits for | `board` action `edit` with `dependsOn` | An unfinished dependency refuses a claim and a move to `doing` or `done`, and the reason line names it |
|
|
33
|
+
| Hand a card to a teammate | `Assign to` in the card dialog | The owner is recorded and the card's history says no handoff was sent — `team`'s `assign` is the one that messages a peer |
|
|
34
|
+
| Take a card back from the tool | `board` action `reclaim` | The owner clears and the card returns to To do, without the page and without `team` |
|
|
35
|
+
| Find a card without reading every lane | `board` action `list` with `query` | The cards whose title, path hint, or holder matches — the same rule the page's filter uses |
|
|
36
|
+
| See the work you hold | `board` action `list` with `mine` | The cards this session holds, by the same rule a claim is checked against |
|
|
37
|
+
| Delete a card | `board` action `drop` | The card and its notes are gone; a `todo` or `done` card is anyone's to drop, one in flight only its holder's, and one that a card waits on cannot leave at all |
|
|
38
|
+
| One page per repository | `/board` | Serves a loopback page as `team-lead` with a **Board** and a **Team** tab, following the store live; a page already served is opened instead |
|
|
39
|
+
| Watch somebody else's page | `/board` on a session that is not serving | The same page, **read-only**: no **+ New card**, no `Claim`, `Move`, `Edit`, `Drop` or note field, because the note a viewer reads carries a credential that may only read. The viewer joins the team under its own label, so a handoff can still reach it |
|
|
40
|
+
| See the lead's round at a glance | `/board team`, or `/team` with no browser | A queue of cards that need a decision, each with the reason and its way out, then who is on the team and what they hold |
|
|
41
|
+
| Join the team | `/board`, or any card you work | Opening the page joins this session to the team — as `team-lead` when it serves the page, under its own name when it watches one — and the first card write joins it too, so nothing has to be armed by hand. `/board status` says which team a `/board` in this session put you on |
|
|
42
|
+
| See who is around | `/board team` | The Teammates list: every session this repository's bus has recorded, plus every owner a card names, each stating `live`, `quiet`, `no record`, or `—` when no bus is loaded |
|
|
43
|
+
|
|
44
|
+
## Quickstart
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
you: publish two features for the billing work
|
|
48
|
+
→ board add "Billing webhook accepts orders" paths ["src/billing"]
|
|
49
|
+
→ board add "Retry branches merge cleanly"
|
|
50
|
+
lead: board edit 2 dependsOn ["1"] priority "high"
|
|
51
|
+
→ #2 waits on #1 (todo), and sorts first in To do
|
|
52
|
+
peer: claim the first, plan it, note the phases, finish it
|
|
53
|
+
→ board claim 1 → /plan → board plan 1 <plan file> → board note 1 … → board move 1 done
|
|
54
|
+
→ board claim 2 — refused while #1 is unfinished, allowed once it is done
|
|
55
|
+
lead: board list round: true
|
|
56
|
+
→ 1 owner · 2 features · 0 unassigned · 1 unplanned
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
| Command | Who | Effect |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `/board` | lead | Serves the page as `team-lead`, or opens the one another session already serves |
|
|
64
|
+
| `/board team` | lead | The same, opened on the Team tab |
|
|
65
|
+
| `/board status` | both | One line: the seat, the port, how many tabs are watching, the store, and the team the last `/board` joined — or the page being watched |
|
|
66
|
+
| `/board stop` | lead | Stops the page this session serves; on a watching session it says who serves it |
|
|
67
|
+
| `/team` | both | The round in the terminal: the rollup, then who is live and who has gone quiet |
|
|
68
|
+
|
|
69
|
+
**Who serves the page.** The first `/board` in a repository serves it; after that the
|
|
70
|
+
page is found by the note it leaves beside the cards (`page.json`, in the board's own
|
|
71
|
+
directory), so a second session opens the same page rather than a rival one. That holds
|
|
72
|
+
while a page answers; if nobody is serving, the next `/board` serves — the seat's name
|
|
73
|
+
does not change, so a card a browser claimed is still the seat's. A note a session left
|
|
74
|
+
behind when it died is not a dead end either: `/board` says which page it named — with its
|
|
75
|
+
URL — takes the note over, and serves.
|
|
76
|
+
|
|
77
|
+
**Joining.** One thing, done for you: `/board` puts this session on the team — as
|
|
78
|
+
`team-lead` when it serves the page, under its own name when it watches one — and so does
|
|
79
|
+
the first card you work. The join is also the listening: mail is reported at that
|
|
80
|
+
session's next turn, and no turn is raised, so being on the team never spends a turn. A
|
|
81
|
+
session that has neither opened the board nor touched a card is not on it. `/board
|
|
82
|
+
status` reports the join `/board` made itself: a session that joined by writing a card
|
|
83
|
+
is on the team, and its status line does not say so.
|
|
84
|
+
|
|
85
|
+
The page is still a seat: it writes as `team-lead` with an id of its own, so a card it
|
|
86
|
+
claimed comes back with `reclaim` rather than a `release` from the terminal.
|
|
87
|
+
|
|
88
|
+
## Tool
|
|
89
|
+
|
|
90
|
+
`board` is one action-shaped tool. `readOnlyHint: false`, `destructiveHint: true`,
|
|
91
|
+
`openWorldHint: false`, `executionMode: "sequential"`.
|
|
92
|
+
|
|
93
|
+
| Action | Parameters | Result |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `list` | `status?`, `mine?`, `card?`, `query?`, `round?` | the cards in lane order, one card, or what a query or `mine` matches; `round: true` adds the rollup, which always covers the whole board |
|
|
96
|
+
| `add` | `title`, `paths?`, `priority?`, `note?` | the new `todo` card |
|
|
97
|
+
| `claim` | `card` | the card, now `doing` and owned by this session |
|
|
98
|
+
| `release` | `card` | the card, back in `todo` |
|
|
99
|
+
| `reclaim` | `card` | the card, back in `todo`, whoever held it |
|
|
100
|
+
| `move` | `card`, `status` | the card in its new lane |
|
|
101
|
+
| `note` | `card`, `text` | the card with the note appended |
|
|
102
|
+
| `plan` | `card`, `path` | the card with its plan recorded |
|
|
103
|
+
| `edit` | `card`, `title?`, `paths?`, `priority?`, `dependsOn?` | the card with what it says corrected; an omitted field is left alone, and `dependsOn: []` removes every link |
|
|
104
|
+
| `drop` | `card` | the id that was dropped |
|
|
105
|
+
|
|
106
|
+
`card` takes `4` or `#4`. A refused call is an error result that still carries the
|
|
107
|
+
card, so the next attempt starts from the truth.
|
|
108
|
+
|
|
109
|
+
## Routes
|
|
110
|
+
|
|
111
|
+
Every route but three needs a token, and which one a request carries decides what it may
|
|
112
|
+
do: the lead's satisfies every route and is in the URL its browser was opened at; the one
|
|
113
|
+
the note carries satisfies the reads and gets a 403 on every write. The three that do not
|
|
114
|
+
are the page's own furniture — `/favicon.ico`, `/theme.js`, and `/app.js` are the same
|
|
115
|
+
bytes to every reader and can neither read nor change a card, so refusing them to a
|
|
116
|
+
fetch that has no token would only break the page. A write is made as the page's
|
|
117
|
+
seat — `team-lead` — not as the session that opened it.
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
GET /favicon.ico 204 and no body — no token
|
|
121
|
+
GET /theme.js the theme script — no token
|
|
122
|
+
GET /app.js the client script — no token
|
|
123
|
+
GET / the document (?view=board|team)
|
|
124
|
+
GET /api/board the snapshot
|
|
125
|
+
GET /events the live frame stream
|
|
126
|
+
POST /api/cards add a card
|
|
127
|
+
POST /api/cards/:id/… claim, release, reclaim, assign, move, note, plan, edit
|
|
128
|
+
DELETE /api/cards/:id drop a card
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Layout
|
|
132
|
+
|
|
133
|
+
One page, two tabs, one lifecycle. The kit shell keeps the toolbar and the footer
|
|
134
|
+
pinned and makes the main pane the only scroll box; the page adds no second one.
|
|
135
|
+
|
|
136
|
+
| Band | What it holds |
|
|
137
|
+
|---|---|
|
|
138
|
+
| Toolbar | the mark, the seat the page writes as, as a chip, a live badge (`connecting…`, `live` or `live · 3m`, `reconnecting…`), then one group: the Board\|Team switch, done-of-total progress, a filter, **+ New card**, the keys button, and the theme toggle |
|
|
139
|
+
| New card dialog | the modal the toolbar's button (or `n`) opens: **Title** on its own line, the loudest field on the page, with a `n/120` count once you type; **Paths** under it, space-separated hints; **Priority** as a three-word list; the hint line; and one closer, `Cancel`, beside **Add card**. The title is focused on open, the dialog stays open after an add so a lead can publish several cards, and a draft survives `Esc`, the backdrop, and `Cancel` is the only thing that discards it. The same dialog is the card dialog's **Edit**: prefilled, headed `Edit card #4`, with **Save** in place of Add — and leaving that mode throws its text away, because that text was the card's, not a draft |
|
|
140
|
+
| Lanes | trays, not a bare list: four columns above 1180px, two at 1180px or narrower, one lane plus the lane switch at 860px or narrower — where it opens on the first lane that holds a card. Each head sticks while the pane scrolls, carries its count as a pill, and folds its cards away |
|
|
141
|
+
| Cards | the id and a two-line title (the page's loudest text), an owner chip and an age, a muted priority chip when it is not `normal`, path chips, one reason line in one voice — what a card waits for, a blocked card's last note (or `blocked, with nothing recorded`), `no plan recorded`, `planned`, `✓ done by <owner>`, and the last note of a card that is none of those — clamped, with the whole sentence as its tooltip, and at most three actions — `Claim` or `Move` bordered, then `Note` and `Open`. A card that waits offers no `Claim`, because the claim would be refused: the reason line is what it says instead, and `Move` is drawn on any card still in flight, as the way into its dialog rather than a permission. A viewer's page draws `Open` alone, and no **+ New card**: the document says `data-writes="0"` and every affordance reads it. A hovered card lifts a pixel; a finished one recedes, title and owner chip muted |
|
|
142
|
+
| Team | two panels and a strip. **Needs a decision** is one row per card — a card that is both idle and unplanned arrives once, keeping its strongest reason — laid out as two lines: the id, title, owner chip and `Reclaim` (or `Claim` when nobody holds it) on the first, the reason on the second with the whole sentence as its tooltip, and its kind as a left edge (`--danger` blocked, `--warn` idle, `--dim` unplanned or waiting, the same `--dim` dashed for nobody's). An unassigned card that waits on unfinished work is a row of its own kind, ranked just under a blocker, and offers no action: the claim the rules would refuse is not on the row at all. Above 1180px the queue stops at one screen's height and scrolls inside its panel, so the roster and the Done strip stay on the same page as its top. **Teammates** is one row per person — a state dot, the label, a `you` chip, a `not listening` chip on a live peer whose inbox watch is off, a mail badge, then `live · 3 held · 1 done · oldest 21m` (a quiet session reads `quiet for 2h`, because a word without an age is no reason to reclaim) — sorted you, live, quiet, no record, and an owner whose label nothing answers to keeps its row. Done is one quiet strip whose ids open their cards. Both panels honour the toolbar's filter, and their headings read `shown/total` the way a lane head does. Below 1180px the queue comes first and the roster follows |
|
|
143
|
+
| Card dialog | the lane as a dot and a word beside the title, a meta line, the owner and its priority, the paths, what it waits for, the plan as monospace text, the notes as a timeline (who and when above the sentence, a rule between them), a note field, and `Move to` every lane but the card's own — a waiting card may be parked in `blocked`, and the rules turn down the two that put it back in flight — then `Assign to` for an unowned card, `Release`, `Edit`, `Drop`, `Close` |
|
|
144
|
+
|
|
145
|
+
Colour is a role: `--muted` for `todo`, `--accent` for `doing`, `--danger` for
|
|
146
|
+
`blocked` and destructive choices, `--success` for `done`. The Team tab's decision
|
|
147
|
+
rows sit on the kit's `--raised`, the one step above `--surface` that lets a block read
|
|
148
|
+
as a plane instead of an outline; the Done lane sits on the page itself, so history
|
|
149
|
+
stops competing with the lanes a lead works in. A card takes its colour
|
|
150
|
+
from the lane it sits in — the head's dot, and the reason line's tone — rather than
|
|
151
|
+
from a bar of its own, so one card does not shout twice. The page states a lane and
|
|
152
|
+
a card with `data-status`, so a theme change repaints it without a rule of its own.
|
|
153
|
+
|
|
154
|
+
The board as a posting card — the same rules, drawn to be read at half width:
|
|
155
|
+
|
|
156
|
+
<img src="../../assets/cards/board.png" alt="One board, every session: the page's seat and live badge; four lane trays, each with a dot, a count pill and a fold; one card carrying an id, a title, its age, a priority chip, a path chip, one reason line and its three actions; and the lead's round with two owner lines and the counts unassigned, unplanned, blocked, idle and done" width="100%">
|
|
157
|
+
|
|
158
|
+
*`board`: the lanes, one card's atoms, and the lead's round — three readings of one board, drawn by `screenshot:cards board`.*
|
|
159
|
+
|
|
160
|
+
<img src="../../assets/cards/board-team.png" alt="The three team verbs as three bands: assign sends a handoff to a named peer, whose claim takes the card; offer broadcasts an event to every other live session without recording an owner, so the first claim wins; reclaim returns the card to To do with its notes kept and no handoff sent" width="100%">
|
|
161
|
+
|
|
162
|
+
*`board-team`: `assign`, `offer`, and `reclaim` — the two that reach the bus, and the one that writes the card alone (`screenshot:cards team`).*
|
|
163
|
+
|
|
164
|
+
## Keys
|
|
165
|
+
|
|
166
|
+
| Key | What it does |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `n` | New card: open the dialog and focus the title field |
|
|
169
|
+
| `/` | Filter the cards by title, path, or owner |
|
|
170
|
+
| `j` / `k` | Next or previous card |
|
|
171
|
+
| `Enter` | Open the focused card |
|
|
172
|
+
| `t` | Switch Board and Team |
|
|
173
|
+
| `Esc` | Close a dialog, or clear the filter |
|
|
174
|
+
| `?` | The keyboard sheet |
|
|
175
|
+
|
|
176
|
+
A dialog traps `Tab`, closes on `Esc` or a backdrop click, and returns focus to
|
|
177
|
+
whatever opened it — the kit's `dialogScript`, shared with the review and browse
|
|
178
|
+
pages. A field keeps its keys: `n` inside the note input types an `n`.
|
|
179
|
+
|
|
180
|
+
## Security
|
|
181
|
+
|
|
182
|
+
- The page binds loopback only, answers only its own host names, and refuses a
|
|
183
|
+
state-changing request another site made.
|
|
184
|
+
- There are two credentials, both 128 random bits minted when the page starts and
|
|
185
|
+
compared in constant time: the lead's, which can change a card, and a viewer's, which
|
|
186
|
+
cannot. They are in the URL, never a cookie, and there is no CORS. The note beside the
|
|
187
|
+
cards (`page.json`, owner-only) carries the viewer's and never the lead's.
|
|
188
|
+
- A write is made **as the page's seat** — a browser is not an identity — so
|
|
189
|
+
the toolbar names the session and claim/reclaim ask first.
|
|
190
|
+
- The page writes only the board store under the agent directory. It never writes
|
|
191
|
+
your repository, and it reads the session bus's own records read-only — labels,
|
|
192
|
+
modes, heartbeats, message counts, and whether each is listening — to draw the Teammates list.
|
|
193
|
+
|
|
194
|
+
## Limitations
|
|
195
|
+
|
|
196
|
+
- **Ownership is advisory.** Two sessions can still edit the same file; only
|
|
197
|
+
`session-bus`'s path claims are a lock.
|
|
198
|
+
- **A finished card stays finished.** `done` is terminal for every transition — claim,
|
|
199
|
+
assign, release, move, and plan all refuse it — so a card a dependency was recorded
|
|
200
|
+
against cannot un-finish and reopen the order after the fact. A card finished by
|
|
201
|
+
mistake is dropped and added again; its notes do not come with it.
|
|
202
|
+
- **Links are recorded by the tool.** A link names an id, so the page shows what a
|
|
203
|
+
card waits for but does not edit it; `board edit` records it while the card is being
|
|
204
|
+
planned, and `board drop` refuses to strand one.
|
|
205
|
+
- **The board does not know whether a session is alive.** "Idle" means a `doing`
|
|
206
|
+
card nobody has touched for 30 minutes, not that its process is gone; and the
|
|
207
|
+
Teammates list reads the bus's records, where `live` is a heartbeat inside ten
|
|
208
|
+
minutes, `quiet` is a record whose heartbeat has aged, and `no record` means only
|
|
209
|
+
that the bus holds no record of that label — never that a session is dead. With
|
|
210
|
+
`session-bus` unloaded, or before any session has joined here, every state reads
|
|
211
|
+
`—` and the board works exactly as before.
|
|
212
|
+
- **The seat and your terminal are two actors.** The page writes as `team-lead`, so a
|
|
213
|
+
card a browser claimed is released from the browser or `reclaim`ed from the tool —
|
|
214
|
+
`board release` in a session is a different actor and is refused. The Team tab marks
|
|
215
|
+
your own row from the session you are reading in, not from the seat.
|
|
216
|
+
- **A viewer's page is read-only, and the note is the only credential it carries.** A
|
|
217
|
+
lead who pastes their own URL hands out write access; `/board status` prints the page,
|
|
218
|
+
not that URL.
|
|
219
|
+
- **Nothing prunes.** A `done` card stays until someone drops it; the page shows
|
|
220
|
+
how many bytes the store occupies.
|
|
221
|
+
- **A page claim is the seat's claim.** Two browsers on one page are one owner, and every
|
|
222
|
+
browser that can reach the lead's URL writes as the same seat.
|
|
223
|
+
- **The page stops a minute after its last tab leaves.** Closing the tab to run a
|
|
224
|
+
command and coming back is fine, and a teammate's watching tab counts as one, so a
|
|
225
|
+
page nobody is reading does not answer a port for the rest of the session. `/board`
|
|
226
|
+
starts it again on the same port.
|
|
227
|
+
- **The port is found, or pinned.** With no setting the page prefers a free port in
|
|
228
|
+
4780–5779, falls back to whatever free port the OS gives it if that one is taken, and
|
|
229
|
+
remembers the answer for the session; `board.port` in `bytefly.json` pins it, so a
|
|
230
|
+
bookmarked URL survives a restart. A pinned port that is already taken fails the start —
|
|
231
|
+
`port 4780 is in use — change "port" in the bytefly.json board section, or stop what
|
|
232
|
+
answers on it` — because a quiet fallback would move the page out from under the
|
|
233
|
+
bookmark. A live note still wins: one repository has one page, so this session attaches to
|
|
234
|
+
the port that note names rather than serving a second one.
|
|
235
|
+
- **The note is believed only while it answers.** A page that crashed leaves its note
|
|
236
|
+
behind; the next `/board` probes it, takes it over when nothing replies, and attaches to
|
|
237
|
+
the page that won when two sessions start in the same instant. A note it cannot read at
|
|
238
|
+
all is named with its path — "another session is serving" is only said of a page that was
|
|
239
|
+
seen answering.
|
|
240
|
+
|
|
241
|
+
## Pi integration
|
|
242
|
+
|
|
243
|
+
- **`session-bus`**: a card's owner is the bus label, so a card names a session a
|
|
244
|
+
peer can be addressed by. The board calls one thing on it — the join, through
|
|
245
|
+
`lib/session-tool.ts`, because a command context cannot call a tool and the request
|
|
246
|
+
therefore travels on `pi.events` — and reads its records otherwise, which is why the
|
|
247
|
+
page works with the bus disabled. A failed join is a clause on the `/board` line, not
|
|
248
|
+
a failed page. The seat is a different actor from the session that serves it, which is
|
|
249
|
+
why `/board status` names both.
|
|
250
|
+
- **`plan`**: `board plan` records a plan file's path; the plan extension owns the
|
|
251
|
+
file. The plan is recorded **after** approval, because plan mode makes mutating
|
|
252
|
+
tools unavailable.
|
|
253
|
+
- **`todo`**: the approved plan's steps are seeded into `todo`. Steps never become
|
|
254
|
+
cards.
|
|
255
|
+
- **`worktree`**: the store is keyed by the repository's common git directory, so
|
|
256
|
+
every worktree shares one board.
|
|
257
|
+
- **`bytefly`**: the page's port appears as a `board` status chip, and `doctor`
|
|
258
|
+
reports nothing new — the board needs no binary and no per-machine setup; its one
|
|
259
|
+
setting, `board.port`, is a `bytefly.json` edit.
|
|
260
|
+
|
|
261
|
+
## Design notes
|
|
262
|
+
|
|
263
|
+
- **One JSON file per card**, in `<agentDir>/board/<repo-key>/cards/`. The listing
|
|
264
|
+
is the index, so a reader needs no cache and no daemon.
|
|
265
|
+
- **Create is exclusive**, by hard-linking a temporary file onto the target name,
|
|
266
|
+
so two adds cannot share an id. **Update** re-reads, re-checks the operation's
|
|
267
|
+
own preconditions, then renames over the old file: an operation means "apply when
|
|
268
|
+
this holds", never "force this state".
|
|
269
|
+
- **The page is a dumb renderer** over one snapshot built on the server, pushed
|
|
270
|
+
only when a card's revision, or a teammate's state, mail, or listening flag, moves: a
|
|
271
|
+
heartbeat every ten seconds is not news, and a frame nobody needs costs the page a
|
|
272
|
+
parse.
|
|
273
|
+
- **One page is found, not configured.** A note beside the cards carries the port, the
|
|
274
|
+
seat, and a read-only token; it is created exclusively, so two sessions starting at
|
|
275
|
+
once cannot both serve, and the loser attaches to the winner. The alternative —
|
|
276
|
+
a setting that names the lead — was rejected: the role moves, the seat's name does
|
|
277
|
+
not. The port is the one thing a file may pin: `board.port` states where the page
|
|
278
|
+
listens, never who serves it.
|
|
279
|
+
- **The new card is a dialog, not a fixture.** The page opens on the board, and an
|
|
280
|
+
empty board leaves its own explanation in place rather than covering it with the
|
|
281
|
+
dialog — the empty state's **Add the first card** is what opens it. A card
|
|
282
|
+
is something you ask to write. It is the kit's modal, so `Tab` stays inside it and
|
|
283
|
+
closing returns focus to the button that opened it; a draft survives `Esc` or a
|
|
284
|
+
backdrop click, and `Cancel` is the one way to throw it away.
|
|
285
|
+
- **One edge, stored once.** A link is one id in the waiting card's file and the
|
|
286
|
+
reverse edge is computed over the board, because a `blockedBy` copy of the same edge
|
|
287
|
+
is a copy that drifts; the cycle check and the drop rule are what keep the graph
|
|
288
|
+
whole instead.
|
|
289
|
+
- **A priority is a field, not a lane.** The four lanes are the board's vocabulary and
|
|
290
|
+
a fifth would double the counts and the heads; inside a lane, priority then age is the
|
|
291
|
+
order, and a card says its priority only when it is not the ordinary one.
|
|
292
|
+
- **`edit` leaves an omitted field alone.** It is the one action whose job is "make it
|
|
293
|
+
say this", so an absent field must mean "not this field" — otherwise the page, which
|
|
294
|
+
shows no links, would clear every link it saved.
|
|
295
|
+
- **Rejected:** steps as cards (they flood the board), a shared database (a file per
|
|
296
|
+
card needs no dependency and no lock), an index file (every write maintains it,
|
|
297
|
+
every read can find it stale), drag-and-drop (a lane change is a named `move`), a
|
|
298
|
+
priority lane (four lanes are the vocabulary), and a reverse `blockedBy` field (two
|
|
299
|
+
copies of one edge).
|
|
300
|
+
|
|
301
|
+
## Files
|
|
302
|
+
|
|
303
|
+
| File | Responsibility |
|
|
304
|
+
|---|---|
|
|
305
|
+
| `index.ts` | The factory: gate on `PI_DISABLED_EXTENSIONS`, build the runtime and the page, register both, close on shutdown |
|
|
306
|
+
| `config.ts` | The board's one setting: the port its page is pinned to, clamped, from the `bytefly.json` `board` section |
|
|
307
|
+
| `schema.ts` | The tool's parameters |
|
|
308
|
+
| `types.ts` | The shapes the tool registers and reports: the card an action produced, the cards a list read, the rollup when it was asked for, and the card a refusal carries |
|
|
309
|
+
| `tools.ts` | Action dispatch and the refusal shape |
|
|
310
|
+
| `input.ts` | A card body as both surfaces read it: the `add` fields normalized once, so the tool and the page cannot disagree |
|
|
311
|
+
| `format.ts` | The model-facing text |
|
|
312
|
+
| `rows.ts` | The transcript row grammar |
|
|
313
|
+
| `result.ts` | The transcript block |
|
|
314
|
+
| `page.ts` | The page's lifecycle: the port it remembers or is pinned to, the chip that names it, and the three closes — `/board stop`, the idle grace, and shutdown |
|
|
315
|
+
| `snapshot.ts` | The page's snapshot: the cards, the bus's records, and the store's size, handed to `buildView` |
|
|
316
|
+
| `writes.ts` | The page's write path: one request to one store call, 422 for a field and 409 for a card somebody else moved |
|
|
317
|
+
| `commands.ts` | `/board`, its three words, and the one line `/board status` prints |
|
|
318
|
+
| `seat.ts` | The seat the page serves as: one name, and an id derived from it, so the page is one actor across restarts |
|
|
319
|
+
| `join.ts` | The board's side of the team: the one request that joins for the user, the label it lands on, and how the outcome reads on a `/board` line |
|
|
320
|
+
| `transport/router.ts` | The routes, what a credential may do, and the guards in front of them |
|
|
321
|
+
| `transport/server.ts` | The loopback listener, the frame push, and the close |
|
|
322
|
+
| `view/page.ts` | The document: an inert shell the client fills |
|
|
323
|
+
| `view/styles.ts` | The page's `board-*` layout rules |
|
|
324
|
+
| `view/render.ts` | The client's snapshot → DOM renderer: toolbar, lanes, cards, the dialog's contents |
|
|
325
|
+
| `view/team.ts` | The client's round: the two tables and the Done line |
|
|
326
|
+
| `view/actions.ts` | The client's writes and its two kit dialogs |
|
|
327
|
+
| `view/keys.ts` | The key table, and the bindings that make it true |
|
|
328
|
+
| `view/live.ts` | The client's tabs, new-card dialog, filter, and frame stream |
|
|
329
|
+
| `view/client.ts` | The one script served at `/app.js` |
|
|
330
|
+
|
|
331
|
+
The store, the rules, the rollup, and the snapshot are `lib/board/` — see
|
|
332
|
+
[`lib/README.md`](../../lib/README.md).
|
|
333
|
+
|
|
334
|
+
## Testing
|
|
335
|
+
|
|
336
|
+
`test/suite-manifest.ts` declares the suites:
|
|
337
|
+
`lib/board/{card,links,page-pointer,round,round-text,rules,session,store,view}` for the
|
|
338
|
+
record, the dependency graph, the note a page leaves, the rollup and its text, the card's
|
|
339
|
+
transitions, the store a call opens and the identity it writes as, the files that store
|
|
340
|
+
keeps, and the snapshot, and
|
|
341
|
+
`extensions/board/{input,seat,tools,page,commands,config,extension,transport/router,view/pages}`
|
|
342
|
+
for the card body, the seat, every action, the page's lifecycle over a real socket (a page
|
|
343
|
+
served, attached to, taken over, and refused to a viewer), the command, the `board.port`
|
|
344
|
+
setting, the factory, the routes, and the document and client bundle.
|