@gavin-hu/my-pi-agent 0.19.0 → 0.21.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 +19 -12
- package/README.zh-CN.md +15 -9
- package/docs/extensions/README.md +2 -1
- package/docs/extensions/board.md +329 -151
- package/docs/extensions/bytefly.md +427 -273
- package/docs/extensions/file-browse.md +46 -44
- package/docs/extensions/job.md +1 -2
- package/docs/extensions/plan.md +86 -24
- package/docs/extensions/serve.md +129 -0
- package/docs/extensions/session-bus.md +23 -10
- package/docs/extensions/team.md +56 -28
- package/docs/extensions/wechat.md +2 -1
- package/docs/extensions/worktree.md +30 -15
- package/extensions/board/commands.ts +48 -17
- package/extensions/board/format.ts +107 -9
- package/extensions/board/index.ts +14 -2
- package/extensions/board/join.ts +31 -10
- package/extensions/board/page.ts +202 -156
- package/extensions/board/result.ts +10 -24
- package/extensions/board/rows.ts +1 -1
- package/extensions/board/schema.ts +31 -10
- package/extensions/board/seat.ts +9 -10
- package/extensions/board/snapshot.ts +7 -5
- package/extensions/board/tools.ts +211 -34
- package/extensions/board/transport/router.ts +27 -110
- package/extensions/board/transport/server.ts +76 -56
- package/extensions/board/types.ts +57 -0
- package/extensions/board/view/client.ts +24 -13
- package/extensions/board/view/dialog.ts +43 -0
- package/extensions/board/view/keys.ts +28 -24
- package/extensions/board/view/live.ts +6 -178
- package/extensions/board/view/names.ts +91 -0
- package/extensions/board/view/page.ts +60 -89
- package/extensions/board/view/render.ts +228 -200
- package/extensions/board/view/styles.ts +72 -49
- package/extensions/board/view/team.ts +63 -41
- package/extensions/bytefly/footer/budget.ts +6 -5
- package/extensions/bytefly/footer/collect/entries.ts +12 -4
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -17
- package/extensions/bytefly/footer/config.ts +19 -11
- package/extensions/bytefly/footer/price/price.ts +30 -8
- package/extensions/bytefly/footer/register.ts +5 -5
- package/extensions/bytefly/footer/render/footer.ts +18 -4
- package/extensions/bytefly/footer/render/format.ts +18 -0
- package/extensions/bytefly/footer/render/key.ts +46 -0
- package/extensions/bytefly/footer/render/layout.ts +13 -3
- package/extensions/bytefly/footer/render/lines.ts +23 -7
- package/extensions/bytefly/footer/render/segments.ts +93 -31
- package/extensions/bytefly/footer/types.ts +29 -5
- package/extensions/bytefly/footer/unpriced.ts +42 -17
- package/extensions/bytefly/header/component.ts +4 -20
- package/extensions/bytefly/panel/commands.ts +28 -1
- package/extensions/bytefly/panel/filter.ts +68 -0
- package/extensions/bytefly/panel/format.ts +16 -15
- package/extensions/bytefly/panel/join.ts +56 -13
- package/extensions/bytefly/panel/prices.ts +43 -0
- package/extensions/bytefly/panel/settings.ts +72 -23
- package/extensions/bytefly/panel/tui/entries.ts +26 -8
- package/extensions/bytefly/panel/tui/frame.ts +130 -93
- package/extensions/bytefly/panel/tui/keys.ts +352 -0
- package/extensions/bytefly/panel/tui/list-view.ts +25 -46
- package/extensions/bytefly/panel/tui/panes.ts +76 -0
- package/extensions/bytefly/panel/tui/section-view.ts +126 -0
- package/extensions/bytefly/panel/tui/settings-section.ts +54 -125
- package/extensions/bytefly/panel/tui/settings-view.ts +30 -131
- package/extensions/bytefly/panel/tui/side-pane.ts +224 -0
- package/extensions/bytefly/panel/tui.ts +306 -259
- package/extensions/check/commands.ts +2 -1
- package/extensions/check/render.ts +10 -1
- package/extensions/file-browse/config/config.ts +4 -19
- package/extensions/file-browse/files/classify.ts +136 -0
- package/extensions/file-browse/files/files.ts +8 -547
- package/extensions/file-browse/files/index.ts +90 -0
- package/extensions/file-browse/files/list.ts +261 -0
- package/extensions/file-browse/files/tree.ts +101 -0
- package/extensions/file-browse/index.ts +27 -48
- package/extensions/file-browse/live/live.ts +1 -1
- package/extensions/file-browse/transport/router.ts +12 -6
- package/extensions/file-browse/transport/server.ts +61 -28
- package/extensions/file-browse/view/chrome.ts +4 -4
- package/extensions/file-browse/view/client.ts +3 -1
- package/extensions/file-browse/view/emit.ts +51 -0
- package/extensions/file-browse/view/html.ts +78 -46
- package/extensions/file-browse/view/live-client.ts +2 -2
- package/extensions/file-browse/view/markdown.ts +23 -26
- package/extensions/file-browse/view/palette.ts +2 -2
- package/extensions/file-browse/view/raw-html.ts +27 -22
- package/extensions/file-browse/view/tree.ts +1 -1
- package/extensions/file-browse/view/urls.ts +20 -6
- package/extensions/goal/tui.ts +12 -2
- package/extensions/job/runtime.ts +4 -4
- package/extensions/job/tools.ts +19 -7
- package/extensions/job/tui.ts +2 -28
- package/extensions/plan/commands.ts +2 -2
- package/extensions/plan/feedback.ts +88 -18
- package/extensions/plan/format.ts +10 -3
- package/extensions/plan/index.ts +24 -2
- package/extensions/plan/plan-files.ts +88 -0
- package/extensions/plan/plan-text.ts +114 -0
- package/extensions/plan/plans.ts +19 -23
- package/extensions/plan/policy.ts +10 -5
- package/extensions/plan/runtime.ts +43 -4
- package/extensions/plan/todos.ts +51 -3
- package/extensions/plan/tools/exit.ts +10 -1
- package/extensions/plan/tools/todo-seed.ts +4 -2
- package/extensions/plan/tools/write.ts +12 -1
- package/extensions/plan/tools.ts +2 -2
- package/extensions/plan/types.ts +15 -0
- package/extensions/plan/web-blocks.ts +160 -0
- package/extensions/plan/web-chrome.ts +7 -5
- package/extensions/plan/web-client.ts +271 -325
- package/extensions/plan/web-collab.ts +7 -7
- package/extensions/plan/web-comments.ts +57 -16
- package/extensions/plan/web-composer.ts +91 -0
- package/extensions/plan/web-draft.ts +40 -0
- package/extensions/plan/web-keys.ts +258 -0
- package/extensions/plan/web-markdown.ts +1 -1
- package/extensions/plan/web-outline.ts +212 -0
- package/extensions/plan/web-page.ts +59 -86
- package/extensions/plan/web-payload.ts +188 -0
- package/extensions/plan/web-plan.ts +160 -0
- package/extensions/plan/web-router.ts +0 -1
- package/extensions/plan/web-settle.ts +201 -0
- package/extensions/plan/web-share-session.ts +24 -55
- package/extensions/plan/web-share.ts +19 -37
- package/extensions/plan/web-write.ts +112 -0
- package/extensions/plan/web.ts +77 -130
- package/extensions/rewind/commands/review.ts +2 -1
- package/extensions/serve/config.ts +37 -0
- package/extensions/serve/index.ts +71 -0
- package/extensions/serve/registry.ts +251 -0
- package/extensions/session-bus/join-request.ts +16 -9
- package/extensions/session-bus/result.ts +1 -27
- package/extensions/session-bus/rows.ts +7 -3
- package/extensions/session-bus/runtime.ts +100 -38
- package/extensions/session-bus/store.ts +14 -2
- package/extensions/session-bus/tools.ts +5 -4
- package/extensions/session-bus/tui.ts +1 -1
- package/extensions/session-bus/types.ts +10 -1
- package/extensions/subagent/commands.ts +2 -1
- package/extensions/team/commands.ts +44 -9
- package/extensions/team/format.ts +14 -0
- package/extensions/team/index.ts +2 -1
- package/extensions/team/tools.ts +30 -9
- package/extensions/team/types.ts +17 -0
- package/extensions/todo/index.ts +4 -1
- package/extensions/todo/nudge.ts +8 -2
- package/extensions/todo/tui.ts +21 -3
- package/extensions/wechat/bridge.ts +43 -172
- package/extensions/wechat/intake.ts +245 -0
- package/extensions/worktree/commands.ts +24 -17
- package/extensions/worktree/exit.ts +12 -6
- package/extensions/worktree/git.ts +22 -14
- package/extensions/worktree/guard.ts +15 -285
- package/extensions/worktree/handoff.ts +10 -2
- package/extensions/worktree/index.ts +2 -1
- package/extensions/worktree/lifecycle.ts +2 -0
- package/extensions/worktree/registry.ts +20 -11
- package/extensions/worktree/root-tools.ts +13 -3
- package/extensions/worktree/runtime.ts +8 -2
- package/extensions/worktree/screen.ts +15 -8
- package/extensions/worktree/shell.ts +303 -0
- package/extensions/worktree/state.ts +1 -1
- package/extensions/worktree/status.ts +42 -9
- package/extensions/worktree/tools.ts +19 -13
- package/lib/README.md +9 -7
- package/lib/board/card.ts +192 -3
- package/lib/board/links.ts +26 -2
- package/lib/board/page-pointer.ts +111 -32
- package/lib/board/round-text.ts +11 -2
- package/lib/board/round.ts +25 -20
- package/lib/board/rules.ts +144 -11
- package/lib/board/session.ts +84 -18
- package/lib/board/store.ts +268 -55
- package/lib/board/view.ts +114 -24
- package/lib/bus-request.ts +83 -0
- package/lib/format.ts +22 -0
- package/lib/http/index.ts +11 -7
- package/lib/http/mount.ts +142 -0
- package/lib/http/port.ts +25 -0
- package/lib/http/serve.ts +170 -43
- package/lib/http/server.ts +3 -3
- package/lib/http/sink.ts +34 -9
- package/lib/plan-entry.ts +20 -13
- package/lib/policy.ts +14 -1
- package/lib/requirements.ts +2 -2
- package/lib/session-label.ts +23 -7
- package/lib/session-roster.ts +74 -3
- package/lib/session-tool.ts +37 -21
- package/lib/spawn.ts +37 -3
- package/lib/stray-fields.ts +45 -0
- package/lib/tool-names.ts +13 -0
- package/lib/tui/README.md +6 -3
- package/lib/tui/glyphs.ts +3 -6
- package/lib/tui/render-cache.ts +29 -0
- package/lib/tui/tool.ts +30 -1
- package/lib/web/README.md +52 -2
- package/lib/web/client-runtime.ts +149 -0
- package/lib/web/index.ts +4 -2
- package/lib/web/markup.ts +106 -2
- package/lib/web/names.ts +42 -0
- package/package.json +3 -2
- package/extensions/board/config.ts +0 -36
- package/extensions/board/view/actions.ts +0 -306
- package/extensions/board/writes.ts +0 -99
- package/extensions/bytefly/panel/tui/extensions-view.ts +0 -40
- package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
- package/extensions/job/process.ts +0 -35
|
@@ -12,18 +12,85 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { type Card, cardRef, DEFAULT_PRIORITY, dependsOf, priorityOf } from "../../lib/board/card.ts";
|
|
15
|
-
import type
|
|
15
|
+
import { type BoardIndex, lanesText, openDependentsOf } from "../../lib/board/links.ts";
|
|
16
|
+
import type { Skip } from "../../lib/board/store.ts";
|
|
17
|
+
import { formatAge } from "../../lib/format.ts";
|
|
16
18
|
import { plural } from "../../lib/board/round-text.ts";
|
|
17
19
|
|
|
18
20
|
/** One card: the headline, then the state a reader needs in order to act on it. */
|
|
19
|
-
export function formatCard(card: Card, board?: BoardIndex): string {
|
|
20
|
-
return [`${cardRef(card.id)} ${card.status} · ${card.title}`, ...detailLines(card, board)].join("\n");
|
|
21
|
+
export function formatCard(card: Card, board?: BoardIndex, now?: number): string {
|
|
22
|
+
return [`${cardRef(card.id)} ${card.status} · ${card.title}`, ...detailLines(card, board, now)].join("\n");
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* What a listing cannot show, and therefore has to say.
|
|
27
|
+
*
|
|
28
|
+
* A footnote is the difference between a listing and the board: an omission nobody
|
|
29
|
+
* accounted for reads as a smaller board — or as history that was deleted.
|
|
30
|
+
*/
|
|
31
|
+
export interface ListingNotes {
|
|
32
|
+
/** Finished cards the board holds that a lane-less listing left out. */
|
|
33
|
+
hiddenDone: number;
|
|
34
|
+
/** Cards set aside in the archive, which no board read opens. */
|
|
35
|
+
archived: number;
|
|
36
|
+
/** Card files this build could not read. */
|
|
37
|
+
skipped: readonly Skip[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A board's listing: the cards, then the footnotes a reader cannot see.
|
|
42
|
+
*
|
|
43
|
+
* One body, because the footnotes belong to the listing: a card file this build cannot
|
|
44
|
+
* read, the finished cards a plain listing left out, and the cards an `archive` set aside.
|
|
45
|
+
*/
|
|
46
|
+
export function formatBoard(shown: readonly Card[], notes: ListingNotes): string {
|
|
47
|
+
const lines =
|
|
48
|
+
shown.length === 0 ? [notes.hiddenDone === 0 ? "The board is empty." : "No open cards."] : shown.map(listLine);
|
|
49
|
+
// A finished card is history rather than work, so a listing that names no lane leaves
|
|
50
|
+
// them out; the count is what keeps the omission from reading as a smaller board.
|
|
51
|
+
if (notes.hiddenDone > 0) {
|
|
52
|
+
const them = notes.hiddenDone === 1 ? "it" : "them";
|
|
53
|
+
lines.push(`${plural(notes.hiddenDone, "done card")} hidden; pass status: "done" to read ${them}.`);
|
|
54
|
+
}
|
|
55
|
+
for (const note of [archiveNote(notes.archived), formatSkipped(notes.skipped)]) {
|
|
56
|
+
if (note !== "") lines.push(note);
|
|
57
|
+
}
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The archive's listing: what is set aside, and the files there this build refused.
|
|
63
|
+
*
|
|
64
|
+
* The archive is read on request only, so this has no lane footnote of its own — every
|
|
65
|
+
* card in the pile is finished, and a `done` lane would be a second way to say it.
|
|
66
|
+
*/
|
|
67
|
+
export function formatArchive(shown: readonly Card[], skipped: readonly Skip[]): string {
|
|
68
|
+
const lines = shown.length === 0 ? ["The archive is empty."] : shown.map(listLine);
|
|
69
|
+
const unreadable = formatSkipped(skipped);
|
|
70
|
+
if (unreadable !== "") lines.push(unreadable);
|
|
71
|
+
return lines.join("\n");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The one sentence a board listing adds so its archive is not mistaken for deleted history.
|
|
76
|
+
*
|
|
77
|
+
* Empty when nothing is set aside, so the ordinary board says nothing new.
|
|
78
|
+
*/
|
|
79
|
+
export function archiveNote(count: number): string {
|
|
80
|
+
if (count === 0) return "";
|
|
81
|
+
return `${plural(count, "card")} ${count === 1 ? "is" : "are"} set aside in the archive; pass archived: true to read ${count === 1 ? "it" : "them"}.`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The files the board could not read, or nothing when it read everything.
|
|
86
|
+
*
|
|
87
|
+
* A skipped file is not an absent one: a board written by a newer build would
|
|
88
|
+
* otherwise read as "nobody has planned anything" while the work sits on disk.
|
|
89
|
+
*/
|
|
90
|
+
export function formatSkipped(skipped: readonly Skip[]): string {
|
|
91
|
+
if (skipped.length === 0) return "";
|
|
92
|
+
const files = skipped.map((entry) => `${entry.file} (${entry.why})`).join(", ");
|
|
93
|
+
return `${plural(skipped.length, "file")} this build cannot read: ${files}`;
|
|
27
94
|
}
|
|
28
95
|
|
|
29
96
|
/** The dropped id, which is the only thing left of the card. */
|
|
@@ -31,22 +98,53 @@ export function formatDropped(id: string): string {
|
|
|
31
98
|
return `${cardRef(id)} dropped, with its notes.`;
|
|
32
99
|
}
|
|
33
100
|
|
|
101
|
+
/**
|
|
102
|
+
* The ids an archive set aside, and how to read them again.
|
|
103
|
+
*
|
|
104
|
+
* A move has to name the way back or it reads like a deletion: the card is out of the
|
|
105
|
+
* board and its notes, plan, and landed commit are still in its file.
|
|
106
|
+
*/
|
|
107
|
+
export function formatArchived(ids: readonly string[]): string {
|
|
108
|
+
const theirs = ids.length === 1 ? "its" : "their";
|
|
109
|
+
return `${plural(ids.length, "card")} archived: ${ids.map(cardRef).join(", ")} — out of the board, ${theirs} notes and plan still readable with archived: true.`;
|
|
110
|
+
}
|
|
111
|
+
|
|
34
112
|
/** A one-line listing: the id, the lane, the title, and who holds it. */
|
|
35
113
|
function listLine(card: Card): string {
|
|
36
114
|
const holder = card.owner === undefined ? "unassigned" : card.owner.label;
|
|
37
115
|
const urgency = priorityOf(card) === DEFAULT_PRIORITY ? "" : ` · ${priorityOf(card)}`;
|
|
38
116
|
const waits = dependsOf(card).length === 0 ? "" : ` · waits on ${dependsOf(card).map(cardRef).join(", ")}`;
|
|
39
|
-
|
|
117
|
+
// The two lanes that carry a fact of their own say it here: what a block waits on, and
|
|
118
|
+
// what closed a finished card. Both are one short phrase, so a listing stays one line.
|
|
119
|
+
const blocked = card.blockedBy === undefined ? "" : ` · blocked: ${card.blockedBy}`;
|
|
120
|
+
const landed = card.landed === undefined ? "" : ` · landed ${card.landed.commit}`;
|
|
121
|
+
return `${cardRef(card.id)} ${card.status} · ${card.title} · ${holder}${urgency}${waits}${blocked}${landed}`;
|
|
40
122
|
}
|
|
41
123
|
|
|
42
124
|
/** The state lines under a card's headline. */
|
|
43
|
-
function detailLines(card: Card, board?: BoardIndex): string[] {
|
|
125
|
+
function detailLines(card: Card, board?: BoardIndex, now?: number): string[] {
|
|
44
126
|
const lines: string[] = [];
|
|
45
127
|
if (card.owner !== undefined) lines.push(` owner: ${card.owner.label}`);
|
|
128
|
+
// An offer's age is the fact a reader acts on, which is the one place an age belongs in
|
|
129
|
+
// this text: the value is whether anybody has had time to answer.
|
|
130
|
+
if (card.offeredAt !== undefined && now !== undefined) {
|
|
131
|
+
lines.push(` offered: ${formatAge(now, card.offeredAt)} ago, and nobody took it`);
|
|
132
|
+
}
|
|
133
|
+
if (card.blockedBy !== undefined) lines.push(` blocked: ${card.blockedBy}`);
|
|
134
|
+
if (card.landed !== undefined) lines.push(` landed: ${card.landed.commit}`);
|
|
46
135
|
if (priorityOf(card) !== DEFAULT_PRIORITY) lines.push(` priority: ${priorityOf(card)}`);
|
|
47
136
|
if (card.plan !== undefined) lines.push(` plan: ${card.plan}`);
|
|
48
137
|
if (card.paths.length > 0) lines.push(` paths: ${card.paths.join(", ")}`);
|
|
49
138
|
if (dependsOf(card).length > 0) lines.push(` waits on: ${waitingOf(card, board)}`);
|
|
139
|
+
// The mirror question, for the same board: what leaving this card undone holds up. Only
|
|
140
|
+
// a card with the board in hand can answer it, which is why a bare card read omits it.
|
|
141
|
+
if (board !== undefined) {
|
|
142
|
+
const dependents = openDependentsOf(card.id, board);
|
|
143
|
+
if (dependents.length > 0) {
|
|
144
|
+
const which = dependents.length === 1 ? "card" : "cards";
|
|
145
|
+
lines.push(` waited on by ${dependents.length} ${which}: ${lanesText(dependents)}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
50
148
|
if (card.notes.length > 0) {
|
|
51
149
|
const last = card.notes.at(-1);
|
|
52
150
|
if (last) lines.push(` last note (${last.by}): ${last.text}`);
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
17
17
|
import { isExtensionEnabled } from "../../lib/env.ts";
|
|
18
|
+
import { requestMount } from "../../lib/http/index.ts";
|
|
18
19
|
import { registerCommands } from "./commands.ts";
|
|
19
20
|
import { type BoardPage, createBoardPage } from "./page.ts";
|
|
20
21
|
import { type BoardSession, openBoardSession } from "../../lib/board/session.ts";
|
|
@@ -30,9 +31,20 @@ export interface BoardDeps {
|
|
|
30
31
|
export default function board(pi: ExtensionAPI, deps: BoardDeps = {}): void {
|
|
31
32
|
if (!isExtensionEnabled("board")) return;
|
|
32
33
|
const session = deps.session ?? openBoardSession(pi);
|
|
33
|
-
|
|
34
|
+
/** The session the page serves as; the mount lands on this session's one server. */
|
|
35
|
+
let sessionId = "";
|
|
36
|
+
const page =
|
|
37
|
+
deps.page ??
|
|
38
|
+
createBoardPage(session, {
|
|
39
|
+
mount: (spec) => requestMount(pi, spec),
|
|
40
|
+
session: () => sessionId,
|
|
41
|
+
});
|
|
34
42
|
registerTools(pi, session);
|
|
35
|
-
registerCommands(pi, page);
|
|
43
|
+
registerCommands(pi, session, page);
|
|
44
|
+
|
|
45
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
46
|
+
sessionId = ctx.sessionManager.getSessionId();
|
|
47
|
+
});
|
|
36
48
|
|
|
37
49
|
pi.on("session_shutdown", async () => {
|
|
38
50
|
await page.closeAll();
|
package/extensions/board/join.ts
CHANGED
|
@@ -2,30 +2,51 @@
|
|
|
2
2
|
* Board's side of the team: the one call that puts this session on it.
|
|
3
3
|
*
|
|
4
4
|
* The user's surface is `/board` and `team`; the session bus is the transport under
|
|
5
|
-
* them. So the board joins on the user's behalf — when the page opens, and
|
|
6
|
-
*
|
|
5
|
+
* them. So the board joins on the user's behalf — when the page opens, and the first
|
|
6
|
+
* time this session claims a card — and never asks anybody to run a bus action.
|
|
7
|
+
*
|
|
8
|
+
* The label it asks for is the session's own team name (`BoardSession.who`), so the
|
|
9
|
+
* card, the bus record, and the roster say one word, and joining never renames the
|
|
10
|
+
* session: the footer keeps the name the user gave it.
|
|
7
11
|
*
|
|
8
12
|
* The seam exists for the same reason `team`'s dispatch does: a board suite drives a
|
|
9
13
|
* recording join instead of a bus, and the shipped implementation is one in-process
|
|
10
14
|
* request through `lib/session-tool.ts` (a command context cannot call a tool).
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
17
|
+
import { messageOf } from "../../lib/errors.ts";
|
|
18
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
19
|
+
import type { JoinResult, TeamJoin } from "../../lib/session-tool.ts";
|
|
20
|
+
import type { TeamStanding } from "../../lib/board/session.ts";
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* The join itself is `lib/session-tool.ts`'s, because `team` asks for it too; this
|
|
18
|
-
* module only holds what is the board's —
|
|
19
|
-
*
|
|
24
|
+
* module only holds what is the board's — when it asks, and how the outcome reads on a
|
|
25
|
+
* `/board` line.
|
|
20
26
|
*/
|
|
21
27
|
export type { TeamJoin } from "../../lib/session-tool.ts";
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
30
|
+
* Put this session on the team, once: the first card it claims.
|
|
31
|
+
*
|
|
32
|
+
* Asked only while the standing does not say this session is on the team, so a card
|
|
33
|
+
* write costs one request for the session rather than one per card. A failure is
|
|
34
|
+
* recorded and asked again by the next claim; a bus that is not loaded answers with a
|
|
35
|
+
* sentence, which is a result and not an exception — and a join that did throw still
|
|
36
|
+
* must not undo the card that was already written, so it is swallowed here.
|
|
26
37
|
*/
|
|
27
|
-
export function
|
|
28
|
-
|
|
38
|
+
export async function joinOnClaim(
|
|
39
|
+
standing: TeamStanding,
|
|
40
|
+
team: TeamJoin,
|
|
41
|
+
ctx: ExtensionContext,
|
|
42
|
+
label: string,
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
if (standing.last()?.ok === true) return;
|
|
45
|
+
try {
|
|
46
|
+
standing.record(await team.join(ctx, { label }));
|
|
47
|
+
} catch (error) {
|
|
48
|
+
standing.record({ ok: false, error: messageOf(error) });
|
|
49
|
+
}
|
|
29
50
|
}
|
|
30
51
|
|
|
31
52
|
/**
|