@gavin-hu/my-pi-agent 0.17.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 +61 -45
- package/README.zh-CN.md +54 -42
- package/docs/extensions/README.md +34 -0
- package/docs/extensions/board.md +344 -0
- package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +465 -416
- 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} +66 -53
- package/{extensions/memory/README.md → docs/extensions/memory.md} +74 -63
- package/docs/extensions/plan.md +976 -0
- package/{extensions/question/README.md → docs/extensions/question.md} +54 -28
- 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} +166 -119
- 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} +60 -38
- package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
- package/{extensions/wechat/README.md → docs/extensions/wechat.md} +275 -192
- 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/notify/types.ts +1 -1
- package/extensions/bytefly/panel/commands.ts +40 -7
- package/extensions/bytefly/panel/settings-runtime.ts +0 -6
- package/extensions/bytefly/panel/settings.ts +11 -21
- 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} +13 -4
- 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} +54 -82
- package/extensions/file-browse/live/rev.ts +42 -0
- package/extensions/file-browse/{router.ts → transport/router.ts} +44 -17
- package/extensions/file-browse/transport/server.ts +110 -0
- package/extensions/file-browse/view/chrome.ts +83 -0
- package/extensions/file-browse/{client.ts → view/client.ts} +63 -1
- package/extensions/file-browse/view/diff-html.ts +94 -0
- package/extensions/file-browse/{html.ts → view/html.ts} +32 -158
- package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
- package/extensions/file-browse/{live-client.ts → view/live-client.ts} +47 -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} +31 -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/goal/plan-route.ts +8 -2
- package/extensions/job/index.ts +15 -7
- package/extensions/memory/store.ts +12 -5
- package/extensions/plan/blocks.ts +23 -2
- package/extensions/plan/commands.ts +77 -21
- package/extensions/plan/feedback.ts +409 -63
- package/extensions/plan/index.ts +7 -5
- package/extensions/plan/plan-index.ts +44 -10
- package/extensions/plan/plan-text.ts +51 -24
- package/extensions/plan/plans.ts +162 -57
- package/extensions/plan/policy.ts +20 -12
- package/extensions/plan/review-prompt.ts +93 -0
- package/extensions/plan/review.ts +51 -95
- package/extensions/plan/runtime.ts +22 -10
- package/extensions/plan/sections.ts +112 -0
- package/extensions/plan/steps.ts +63 -95
- package/extensions/plan/tools/enter.ts +8 -3
- package/extensions/plan/tools/exit.ts +13 -19
- package/extensions/plan/tools/write.ts +8 -3
- package/extensions/plan/tools.ts +1 -1
- package/extensions/plan/tui.ts +29 -15
- package/extensions/plan/types.ts +29 -3
- package/extensions/plan/web-chrome.ts +2 -1
- package/extensions/plan/web-client.ts +122 -178
- package/extensions/plan/web-collab.ts +207 -0
- package/extensions/plan/web-comments.ts +269 -0
- package/extensions/plan/web-page.ts +96 -17
- package/extensions/plan/web-router.ts +109 -47
- package/extensions/plan/web-share-session.ts +191 -0
- package/extensions/plan/web-share.ts +169 -0
- package/extensions/plan/web-streams.ts +124 -0
- package/extensions/plan/web-tokens.ts +63 -0
- package/extensions/plan/web.ts +245 -151
- package/extensions/question/dialogs.ts +91 -1
- package/extensions/question/tools.ts +2 -1
- 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 +113 -0
- package/extensions/session-bus/format.ts +101 -0
- package/extensions/session-bus/index.ts +75 -0
- package/extensions/session-bus/join-request.ts +40 -0
- package/extensions/session-bus/mail-watch.ts +134 -0
- package/extensions/session-bus/notice.ts +86 -0
- package/extensions/session-bus/overlap.ts +36 -0
- package/extensions/session-bus/protocol.ts +104 -0
- package/extensions/session-bus/resolve.ts +67 -0
- package/extensions/session-bus/result.ts +208 -0
- package/extensions/session-bus/rows.ts +171 -0
- package/extensions/session-bus/runtime.ts +466 -0
- package/extensions/session-bus/store.ts +365 -0
- package/extensions/session-bus/tools.ts +192 -0
- package/extensions/session-bus/tui.ts +394 -0
- package/extensions/session-bus/types.ts +98 -0
- 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/watch/index.ts +15 -9
- 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} +66 -51
- package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +40 -25
- 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} +119 -56
- package/extensions/wechat/{presence.ts → peer/presence.ts} +11 -11
- package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +13 -5
- 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} +15 -7
- 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} +104 -40
- 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/git.ts +5 -0
- package/extensions/worktree/guard.ts +3 -5
- package/extensions/worktree/registry.ts +11 -5
- package/extensions/worktree/status.ts +1 -3
- package/lib/README.md +12 -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/router.ts +40 -13
- package/lib/http/serve.ts +234 -0
- package/lib/http/server.ts +29 -13
- package/lib/http/sink.ts +110 -0
- package/lib/http/token.ts +38 -0
- package/lib/interaction.ts +10 -0
- package/lib/notice.ts +29 -2
- package/lib/plan-entry.ts +7 -2
- package/lib/policy.ts +12 -2
- package/lib/qr.ts +64 -0
- package/lib/repo-key.ts +74 -0
- package/lib/report.ts +52 -33
- package/lib/requirements.ts +7 -9
- 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/README.md +16 -2
- package/lib/tui/dock-screen.ts +85 -17
- package/lib/tui/glyphs.ts +2 -0
- package/lib/tui/line-pane.ts +15 -4
- package/lib/tui/tool.ts +23 -4
- package/lib/web/README.md +5 -2
- package/lib/web/components.ts +6 -1
- package/lib/web/runtime.ts +7 -1
- package/lib/web/tokens.ts +25 -2
- package/package.json +27 -14
- package/skills/init/SKILL.md +80 -45
- package/skills/init/references/agents-md-quality.md +26 -2
- package/skills/init/references/interview-questions.md +36 -14
- package/skills/next/SKILL.md +77 -51
- 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 -467
- package/extensions/file-browse/server.ts +0 -134
- package/extensions/plan/README.md +0 -709
- package/extensions/plan/config.ts +0 -27
- package/extensions/wechat/outbound.ts +0 -100
- package/extensions/wechat/qrcode.ts +0 -46
- package/skills/changelog/SKILL.md +0 -148
- /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/{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,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/sessions` board: a docked list over the bus, with three scopes.
|
|
3
|
+
*
|
|
4
|
+
* `Tab` cycles Sessions → Inbox → Claims. Sessions and claims come from the bus
|
|
5
|
+
* snapshot, mail from a read-only peek, so the board announces nothing and
|
|
6
|
+
* consumes nothing. The rows, summaries, and focus blocks are in `rows.ts`; this
|
|
7
|
+
* module owns the screen, the scope it shows, and the two verbs that change this
|
|
8
|
+
* session's own state.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
12
|
+
import { matchesKey, Key } from "@earendil-works/pi-tui";
|
|
13
|
+
import { messageOf } from "../../lib/errors.ts";
|
|
14
|
+
import { formatDuration, sanitizeLine } from "../../lib/format.ts";
|
|
15
|
+
import type { ScreenHint, ViewportRowsSource } from "../../lib/tui/chrome.ts";
|
|
16
|
+
import { type DetailContents, DockListScreen } from "../../lib/tui/dock-screen.ts";
|
|
17
|
+
import { SEPARATORS } from "../../lib/tui/glyphs.ts";
|
|
18
|
+
import { keyValueRow } from "../../lib/tui/rows.ts";
|
|
19
|
+
import { paint } from "../../lib/tui/tokens.ts";
|
|
20
|
+
import { idSuffix } from "./format.ts";
|
|
21
|
+
import {
|
|
22
|
+
type SessionRow,
|
|
23
|
+
claimRowLine,
|
|
24
|
+
count,
|
|
25
|
+
messageRowLine,
|
|
26
|
+
sessionDetailBlock,
|
|
27
|
+
sessionRowLine,
|
|
28
|
+
sessionRows,
|
|
29
|
+
summaryCells,
|
|
30
|
+
} from "./rows.ts";
|
|
31
|
+
import type { BoardSnapshot, BusMessage, ClaimRecord } from "./types.ts";
|
|
32
|
+
|
|
33
|
+
/** The scopes `Tab` cycles through. */
|
|
34
|
+
export type BoardScope = "sessions" | "inbox" | "claims";
|
|
35
|
+
|
|
36
|
+
/** One row of the board, whichever scope is showing. */
|
|
37
|
+
type BoardRow =
|
|
38
|
+
| { kind: "session"; row: SessionRow }
|
|
39
|
+
| { kind: "message"; message: BusMessage }
|
|
40
|
+
| { kind: "claim"; claim: ClaimRecord };
|
|
41
|
+
|
|
42
|
+
const SCOPE_ORDER: readonly BoardScope[] = ["sessions", "inbox", "claims"];
|
|
43
|
+
|
|
44
|
+
const SCOPE_TITLE: Readonly<Record<BoardScope, string>> = {
|
|
45
|
+
sessions: "Sessions",
|
|
46
|
+
inbox: "Inbox",
|
|
47
|
+
claims: "Claims",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const SCOPE_EMPTY: Readonly<Record<BoardScope, string>> = {
|
|
51
|
+
sessions: "No sessions on this bus yet. Press w to join and listen.",
|
|
52
|
+
inbox: "No unread mail.",
|
|
53
|
+
claims: "No claims.",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** A relative age for a message or a claim, e.g. `2.0s ago`, `5m00s ago`. */
|
|
57
|
+
function ago(at: number, now: number): string {
|
|
58
|
+
return `${formatDuration(Math.max(0, now - at))} ago`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A message body as prose: one paragraph per source line, control characters gone. */
|
|
62
|
+
function messageProse(message: BusMessage): string {
|
|
63
|
+
return message.text
|
|
64
|
+
.split("\n")
|
|
65
|
+
.map((line) => sanitizeLine(line))
|
|
66
|
+
.join("\n");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The paths a message mentions, or an empty string. */
|
|
70
|
+
function messagePaths(message: BusMessage): string {
|
|
71
|
+
return message.paths?.length ? message.paths.map((path) => sanitizeLine(path)).join(", ") : "";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** The pane's width-dependent facts: identity, above the claim body. */
|
|
75
|
+
function paneFacts(theme: Theme, row: SessionRow, now: number, width: number): string[] {
|
|
76
|
+
const labelWidth = 10;
|
|
77
|
+
return [
|
|
78
|
+
keyValueRow(theme, width, { label: "mode", labelWidth, value: sanitizeLine(row.session.mode) }),
|
|
79
|
+
keyValueRow(theme, width, { label: "id", labelWidth, value: idSuffix(row.session.id) }),
|
|
80
|
+
keyValueRow(theme, width, {
|
|
81
|
+
label: "last seen",
|
|
82
|
+
labelWidth,
|
|
83
|
+
value: ago(row.session.lastSeen, now),
|
|
84
|
+
}),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The pane body: every claim this session holds, in path order. */
|
|
89
|
+
function paneLines(row: SessionRow, now: number): string[] {
|
|
90
|
+
return row.claims.map((claim) => `${sanitizeLine(claim.path)}${SEPARATORS.item}held ${ago(claim.at, now)}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A cheap signature of what the board draws, so a poll repaints only on a change. */
|
|
94
|
+
function boardKey(board: BoardSnapshot | undefined): string {
|
|
95
|
+
if (!board) return "";
|
|
96
|
+
return [
|
|
97
|
+
...board.status.sessions.map(
|
|
98
|
+
(session) =>
|
|
99
|
+
`${session.id}:${session.label}:${session.unread}:${session.lastSeen}:${session.mode}:${session.cwd}`,
|
|
100
|
+
),
|
|
101
|
+
...board.status.claims.map((claim) => `${claim.path}:${claim.owner}:${claim.ownerLabel}:${claim.at}`),
|
|
102
|
+
board.status.watching ? `${board.status.watching.armed}:${board.status.watching.wake}` : "",
|
|
103
|
+
...board.messages.map((message) => `${message.id}:${message.at}`),
|
|
104
|
+
].join("|");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface SessionListCallbacks {
|
|
108
|
+
/** Arm (or disarm) this session's inbox watch, returning the line to show. */
|
|
109
|
+
toggleWatch(armed: boolean): Promise<string>;
|
|
110
|
+
/** Release one of this session's claims, returning the line to show. */
|
|
111
|
+
releaseClaim(path: string): Promise<string>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface SessionListOptions {
|
|
115
|
+
/** The latest board, or undefined before the first read. */
|
|
116
|
+
latest(): BoardSnapshot | undefined;
|
|
117
|
+
theme: Theme;
|
|
118
|
+
callbacks: SessionListCallbacks;
|
|
119
|
+
onClose(): void;
|
|
120
|
+
requestRender(): void;
|
|
121
|
+
now?: () => number;
|
|
122
|
+
viewportRowsSource?: ViewportRowsSource;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The `/sessions` board.
|
|
127
|
+
*
|
|
128
|
+
* The rows are derived from a board the command's poll supplies, so the screen
|
|
129
|
+
* never reads the bus itself; `liveDetail` keeps an open pane in step with that
|
|
130
|
+
* board, so a peer that expires or a message that is consumed closes its own pane.
|
|
131
|
+
* The focus is anchored by a scope-prefixed id, so switching scopes lands on the
|
|
132
|
+
* first row of the new list instead of a row that is no longer there.
|
|
133
|
+
*/
|
|
134
|
+
export class SessionListComponent extends DockListScreen<BoardRow> {
|
|
135
|
+
private board: BoardSnapshot | undefined;
|
|
136
|
+
private scope: BoardScope = "sessions";
|
|
137
|
+
private readonly callbacks: SessionListCallbacks;
|
|
138
|
+
private readonly now: () => number;
|
|
139
|
+
|
|
140
|
+
constructor(options: SessionListOptions) {
|
|
141
|
+
super({
|
|
142
|
+
theme: options.theme,
|
|
143
|
+
title: "Sessions",
|
|
144
|
+
detailTitle: "Details",
|
|
145
|
+
emptyList: "No sessions on this bus.",
|
|
146
|
+
emptyDetail: "Nothing to show.",
|
|
147
|
+
requestRender: options.requestRender,
|
|
148
|
+
onClose: options.onClose,
|
|
149
|
+
viewportRowsSource: options.viewportRowsSource,
|
|
150
|
+
});
|
|
151
|
+
this.callbacks = options.callbacks;
|
|
152
|
+
this.now = options.now ?? Date.now;
|
|
153
|
+
this.board = options.latest();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Replace the board; a poll that changed nothing does not repaint. */
|
|
157
|
+
setBoard(board: BoardSnapshot | undefined): void {
|
|
158
|
+
const before = boardKey(this.board);
|
|
159
|
+
this.board = board;
|
|
160
|
+
if (boardKey(board) !== before) this.options.requestRender();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The scope currently listed, for a test or a caller that wants to label it. */
|
|
164
|
+
get currentScope(): BoardScope {
|
|
165
|
+
return this.scope;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
protected headerLabel(): string {
|
|
169
|
+
return SCOPE_TITLE[this.scope];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected emptyLabel(): string {
|
|
173
|
+
return SCOPE_EMPTY[this.scope];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected rows(): readonly BoardRow[] {
|
|
177
|
+
switch (this.scope) {
|
|
178
|
+
case "sessions":
|
|
179
|
+
return sessionRows(this.board?.status).map((row) => ({ kind: "session", row }));
|
|
180
|
+
case "inbox":
|
|
181
|
+
return (this.board?.messages ?? []).map((message) => ({ kind: "message", message }));
|
|
182
|
+
case "claims":
|
|
183
|
+
return (this.board?.status.claims ?? []).map((claim) => ({ kind: "claim", claim }));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
protected idOf(row: BoardRow): string {
|
|
188
|
+
switch (row.kind) {
|
|
189
|
+
case "session":
|
|
190
|
+
return `s:${row.row.session.id}`;
|
|
191
|
+
case "message":
|
|
192
|
+
return `m:${row.message.id}`;
|
|
193
|
+
case "claim":
|
|
194
|
+
return `c:${row.claim.path}`;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected rowLine(row: BoardRow, width: number, selected: boolean): string {
|
|
199
|
+
switch (row.kind) {
|
|
200
|
+
case "session":
|
|
201
|
+
return sessionRowLine(row.row, this.theme, width, { selected });
|
|
202
|
+
case "message":
|
|
203
|
+
return messageRowLine(row.message, this.theme, width, { selected });
|
|
204
|
+
case "claim":
|
|
205
|
+
return claimRowLine(row.claim, this.theme, width, {
|
|
206
|
+
selected,
|
|
207
|
+
self: row.claim.owner === this.me(),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
protected detailBlock(row: BoardRow): string[] {
|
|
213
|
+
switch (row.kind) {
|
|
214
|
+
case "session":
|
|
215
|
+
return sessionDetailBlock(row.row, this.now());
|
|
216
|
+
case "message": {
|
|
217
|
+
const lines = [`received: ${ago(row.message.at, this.now())}`];
|
|
218
|
+
const paths = messagePaths(row.message);
|
|
219
|
+
if (paths) lines.push(`paths: ${paths}`);
|
|
220
|
+
return lines;
|
|
221
|
+
}
|
|
222
|
+
case "claim":
|
|
223
|
+
return [`held ${ago(row.claim.at, this.now())}`];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
protected summaryCells(rows: readonly BoardRow[]): string[] {
|
|
228
|
+
const watching = this.board?.status.watching;
|
|
229
|
+
switch (this.scope) {
|
|
230
|
+
case "sessions": {
|
|
231
|
+
const sessions = rows.flatMap((row) => (row.kind === "session" ? [row.row] : []));
|
|
232
|
+
return summaryCells(this.theme, sessions, watching, this.board?.status.onBus !== false);
|
|
233
|
+
}
|
|
234
|
+
case "inbox": {
|
|
235
|
+
const cells = [paint(this.theme, "meta", count(rows.length, "unread message"))];
|
|
236
|
+
if (watching) cells.push(this.watchCell(watching));
|
|
237
|
+
return cells;
|
|
238
|
+
}
|
|
239
|
+
case "claims": {
|
|
240
|
+
const own = rows.flatMap((row) => (row.kind === "claim" && row.claim.owner === this.me() ? [row] : []));
|
|
241
|
+
const cells = [paint(this.theme, "meta", count(rows.length, "claim"))];
|
|
242
|
+
if (own.length > 0) cells.push(paint(this.theme, "label", `${own.length} yours`));
|
|
243
|
+
return cells;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
protected listHints(): ScreenHint[] {
|
|
249
|
+
const tab = (scope: BoardScope): ScreenHint => ({ text: `Tab ${SCOPE_TITLE[scope].toLowerCase()}` });
|
|
250
|
+
const enter = (verb: string): ScreenHint => ({ text: `Enter ${verb}`, color: "accent" });
|
|
251
|
+
const hints: ScreenHint[] = ["Esc", tab(this.nextScope())];
|
|
252
|
+
switch (this.scope) {
|
|
253
|
+
case "sessions":
|
|
254
|
+
hints.push(enter("open"), this.watchHint(), "↑/↓", "PgUp/PgDn");
|
|
255
|
+
break;
|
|
256
|
+
case "inbox":
|
|
257
|
+
hints.push(enter("read"), this.watchHint(), "↑/↓", "PgUp/PgDn");
|
|
258
|
+
break;
|
|
259
|
+
case "claims":
|
|
260
|
+
hints.push(enter("open"), { text: "x release", color: "warning" }, "↑/↓", "PgUp/PgDn");
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
return hints;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
protected detailHints(row: BoardRow | undefined): ScreenHint[] {
|
|
267
|
+
if (row?.kind === "claim" && row.claim.owner === this.me()) {
|
|
268
|
+
return ["Esc back", { text: "x release", color: "warning" }, "↑/↓ scroll", "g/G ends"];
|
|
269
|
+
}
|
|
270
|
+
return ["Esc back", "↑/↓ scroll", "g/G ends", "PgUp/PgDn"];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
protected detailFor(row: BoardRow): DetailContents {
|
|
274
|
+
switch (row.kind) {
|
|
275
|
+
case "session":
|
|
276
|
+
return {
|
|
277
|
+
title: `${sanitizeLine(row.row.session.label)}${row.row.self ? " · you" : ""}`,
|
|
278
|
+
subtitle: sanitizeLine(row.row.session.cwd),
|
|
279
|
+
lines: paneLines(row.row, this.now()),
|
|
280
|
+
tail: false,
|
|
281
|
+
facts: (width) => paneFacts(this.theme, row.row, this.now(), width),
|
|
282
|
+
};
|
|
283
|
+
case "message": {
|
|
284
|
+
const paths = messagePaths(row.message);
|
|
285
|
+
return {
|
|
286
|
+
title: `${sanitizeLine(row.message.fromLabel)}${SEPARATORS.item}${sanitizeLine(row.message.kind)}`,
|
|
287
|
+
subtitle: `${ago(row.message.at, this.now())}${paths ? `${SEPARATORS.item}paths: ${paths}` : ""}`,
|
|
288
|
+
text: messageProse(row.message),
|
|
289
|
+
tail: false,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
case "claim": {
|
|
293
|
+
// A claim's pane is the session that holds it: identity plus everything it holds.
|
|
294
|
+
const owner = this.ownerRow(row.claim.owner);
|
|
295
|
+
return {
|
|
296
|
+
title: sanitizeLine(row.claim.ownerLabel),
|
|
297
|
+
subtitle: `holds ${sanitizeLine(row.claim.path)}${SEPARATORS.item}held ${ago(row.claim.at, this.now())}`,
|
|
298
|
+
lines: owner ? paneLines(owner, this.now()) : [],
|
|
299
|
+
tail: false,
|
|
300
|
+
...(owner ? { facts: (width: number) => paneFacts(this.theme, owner, this.now(), width) } : {}),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
protected detailById(id: string): { lines: string[] } | undefined {
|
|
307
|
+
const row = this.rows().find((candidate) => this.idOf(candidate) === id);
|
|
308
|
+
if (!row) return undefined;
|
|
309
|
+
switch (row.kind) {
|
|
310
|
+
case "session":
|
|
311
|
+
return { lines: paneLines(row.row, this.now()) };
|
|
312
|
+
case "claim": {
|
|
313
|
+
const owner = this.ownerRow(row.claim.owner);
|
|
314
|
+
return { lines: owner ? paneLines(owner, this.now()) : [] };
|
|
315
|
+
}
|
|
316
|
+
case "message":
|
|
317
|
+
// The pane's body is prose, which the base owns; its presence is all this answers.
|
|
318
|
+
return { lines: [] };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
protected liveDetail(): boolean {
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
protected handleVerb(key: string, focused: BoardRow | undefined): void {
|
|
327
|
+
if (matchesKey(key, Key.tab)) {
|
|
328
|
+
this.cycleScope();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (key === "w") {
|
|
332
|
+
this.toggleWatch();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (key === "x" && focused?.kind === "claim") this.release(focused.claim);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
protected handleDetailVerb(key: string, row: BoardRow): boolean {
|
|
339
|
+
if (key !== "x" || row.kind !== "claim") return false;
|
|
340
|
+
this.release(row.claim);
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private me(): string {
|
|
345
|
+
return this.board?.status.me.id ?? "";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** The row for the session with `id`, when it is still on the board. */
|
|
349
|
+
private ownerRow(id: string): SessionRow | undefined {
|
|
350
|
+
return sessionRows(this.board?.status).find((row) => row.session.id === id);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private nextScope(): BoardScope {
|
|
354
|
+
const index = SCOPE_ORDER.indexOf(this.scope);
|
|
355
|
+
return SCOPE_ORDER[(index + 1) % SCOPE_ORDER.length] ?? "sessions";
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private cycleScope(): void {
|
|
359
|
+
this.scope = this.nextScope();
|
|
360
|
+
this.options.requestRender();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private watchCell(watching: { armed: boolean; wake: boolean }): string {
|
|
364
|
+
return paint(this.theme, "ok", watching.wake ? "watch wake" : "watch on");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private watchHint(): ScreenHint {
|
|
368
|
+
return this.board?.status.watching
|
|
369
|
+
? { text: "w watch off", color: "warning" }
|
|
370
|
+
: { text: "w watch on", color: "accent" };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private toggleWatch(): void {
|
|
374
|
+
const armed = this.board?.status.watching !== undefined;
|
|
375
|
+
void this.callbacks
|
|
376
|
+
.toggleWatch(armed)
|
|
377
|
+
.then((line) => this.notify(line))
|
|
378
|
+
.catch((error) => this.notify(messageOf(error)));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private release(claim: ClaimRecord): void {
|
|
382
|
+
const path = sanitizeLine(claim.path);
|
|
383
|
+
if (claim.owner !== this.me()) {
|
|
384
|
+
this.notify(`${path} is ${sanitizeLine(claim.ownerLabel)}'s claim; not released.`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
this.confirm(`Release ${path}? y/N`, () => {
|
|
388
|
+
void this.callbacks
|
|
389
|
+
.releaseClaim(claim.path)
|
|
390
|
+
.then((line) => this.notify(line))
|
|
391
|
+
.catch((error) => this.notify(messageOf(error)));
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared shapes for the session bus.
|
|
3
|
+
*
|
|
4
|
+
* These describe the files and the tool result only; the file I/O lives in
|
|
5
|
+
* `store.ts` and the lifecycle in `runtime.ts`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MessageKind, SessionMode } from "./protocol.ts";
|
|
9
|
+
|
|
10
|
+
/** One registered Pi session. */
|
|
11
|
+
export interface SessionRecord {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
pid: number;
|
|
15
|
+
/** Working directory of the session; the worktree root when there is one. */
|
|
16
|
+
cwd: string;
|
|
17
|
+
/** Repository root the session belongs to, for relative claims. */
|
|
18
|
+
repoRoot: string;
|
|
19
|
+
/** The mode the session was started in. */
|
|
20
|
+
mode: SessionMode;
|
|
21
|
+
startedAt: number;
|
|
22
|
+
lastSeen: number;
|
|
23
|
+
/**
|
|
24
|
+
* Whether this session is listening for mail, written when it joins.
|
|
25
|
+
*
|
|
26
|
+
* Absent means "not listening": a record written before this existed and a session
|
|
27
|
+
* that stopped listening read the same way, which is the honest answer for a peer
|
|
28
|
+
* deciding whether mail will reach it.
|
|
29
|
+
*/
|
|
30
|
+
watching?: { armed: boolean; wake: boolean };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One message waiting in a recipient's inbox. */
|
|
34
|
+
export interface BusMessage {
|
|
35
|
+
id: string;
|
|
36
|
+
from: string;
|
|
37
|
+
fromLabel: string;
|
|
38
|
+
/** Recipient session id. */
|
|
39
|
+
to: string;
|
|
40
|
+
toLabel: string;
|
|
41
|
+
kind: MessageKind;
|
|
42
|
+
text: string;
|
|
43
|
+
paths?: string[];
|
|
44
|
+
at: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One repository-relative claim. */
|
|
48
|
+
export interface ClaimRecord {
|
|
49
|
+
path: string;
|
|
50
|
+
owner: string;
|
|
51
|
+
ownerLabel: string;
|
|
52
|
+
at: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** What `session status` returns. */
|
|
56
|
+
export interface SessionStatus {
|
|
57
|
+
me: { id: string; label: string };
|
|
58
|
+
/** False while this session has only read the bus: it joins by naming itself. */
|
|
59
|
+
onBus: boolean;
|
|
60
|
+
sessions: Array<SessionRecord & { unread: number }>;
|
|
61
|
+
claims: ClaimRecord[];
|
|
62
|
+
/** Present only when this session armed its inbox watch. */
|
|
63
|
+
watching?: { armed: boolean; wake: boolean };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A conflict returned by `session claim`. */
|
|
67
|
+
export interface ClaimConflict {
|
|
68
|
+
path: string;
|
|
69
|
+
owner: string;
|
|
70
|
+
ownerLabel: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* What the board reads in one poll: the bus as it stands, and this session's
|
|
75
|
+
* unread mail. Peeking never consumes, so the notice and `inbox` are unaffected.
|
|
76
|
+
*/
|
|
77
|
+
export interface BoardSnapshot {
|
|
78
|
+
status: SessionStatus;
|
|
79
|
+
messages: BusMessage[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Structured result carried in the `session` tool's details. */
|
|
83
|
+
export interface SessionDetails {
|
|
84
|
+
action: "status" | "send" | "inbox" | "claim" | "release" | "join";
|
|
85
|
+
status?: SessionStatus;
|
|
86
|
+
delivered?: string[];
|
|
87
|
+
messageId?: string;
|
|
88
|
+
messages?: BusMessage[];
|
|
89
|
+
claimed?: string[];
|
|
90
|
+
conflicts?: ClaimConflict[];
|
|
91
|
+
released?: string[];
|
|
92
|
+
notOwned?: string[];
|
|
93
|
+
/** Whether the inbox watch is on, and whether it raises a turn; `join` reports it. */
|
|
94
|
+
watching?: { armed: boolean; wake: boolean };
|
|
95
|
+
/** With join: the label the bus settled on, which is what this session is called. */
|
|
96
|
+
label?: string;
|
|
97
|
+
error?: string;
|
|
98
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
19
|
+
import { READ_DOC_TOOL, WEB_FETCH_TOOL, WEB_SEARCH_TOOL } from "../../lib/tool-names.ts";
|
|
19
20
|
|
|
20
21
|
/** Where an agent definition came from. */
|
|
21
22
|
export type AgentSource = "builtin" | "user" | "project";
|
|
@@ -44,7 +45,7 @@ export const BUILTIN_AGENTS: readonly AgentConfig[] = [
|
|
|
44
45
|
{
|
|
45
46
|
name: "explorer",
|
|
46
47
|
description: "Fast codebase recon that returns compressed context for handoff to other agents",
|
|
47
|
-
tools: ["read", "grep", "find", "ls"],
|
|
48
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL],
|
|
48
49
|
source: "builtin",
|
|
49
50
|
systemPrompt: `You are an explorer. Quickly investigate a codebase and return structured findings that another agent can use without re-reading everything.
|
|
50
51
|
|
|
@@ -94,7 +95,7 @@ Which file to look at first and why.`,
|
|
|
94
95
|
name: "planner",
|
|
95
96
|
description:
|
|
96
97
|
"Delegated implementation planning in an isolated context (headless/chain; use plan mode for interactive planning)",
|
|
97
|
-
tools: ["read", "grep", "find", "ls"],
|
|
98
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL],
|
|
98
99
|
source: "builtin",
|
|
99
100
|
systemPrompt: `You are a delegated planning specialist working in an isolated context. The caller hands you planning when it wants a plan produced without adding exploration to its own transcript — for example an explorer → planner → worker chain, or a headless run.
|
|
100
101
|
|
|
@@ -134,7 +135,7 @@ Keep the plan concrete. The worker agent will execute it verbatim.`,
|
|
|
134
135
|
{
|
|
135
136
|
name: "reviewer",
|
|
136
137
|
description: "Code review specialist for quality and security analysis",
|
|
137
|
-
tools: ["read", "grep", "find", "ls", "bash"],
|
|
138
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
138
139
|
source: "builtin",
|
|
139
140
|
systemPrompt: `You are a senior code reviewer. Analyze code for quality, security, and maintainability.
|
|
140
141
|
|
|
@@ -168,7 +169,7 @@ Be specific with file paths and line numbers.`,
|
|
|
168
169
|
{
|
|
169
170
|
name: "worker",
|
|
170
171
|
description: "General-purpose subagent with full capabilities and an isolated context window",
|
|
171
|
-
tools: ["read", "write", "edit", "bash", "grep", "find", "ls"],
|
|
172
|
+
tools: ["read", "write", "edit", "bash", "grep", "find", "ls", READ_DOC_TOOL],
|
|
172
173
|
source: "builtin",
|
|
173
174
|
systemPrompt: `You are a worker agent with full capabilities. You operate in an isolated context window to handle delegated tasks without polluting the main conversation.
|
|
174
175
|
|
|
@@ -192,7 +193,7 @@ If handing off to another agent (e.g. reviewer), include:
|
|
|
192
193
|
{
|
|
193
194
|
name: "researcher",
|
|
194
195
|
description: "Web research specialist that returns sourced findings on external questions",
|
|
195
|
-
tools: ["read", "grep", "find", "ls",
|
|
196
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL, WEB_SEARCH_TOOL, WEB_FETCH_TOOL],
|
|
196
197
|
source: "builtin",
|
|
197
198
|
systemPrompt: `You are a research specialist. You investigate questions using web_search and web_fetch, then return findings another agent can act on.
|
|
198
199
|
|
|
@@ -228,7 +229,7 @@ What the caller should do with this, in 2-3 sentences.`,
|
|
|
228
229
|
{
|
|
229
230
|
name: "tester",
|
|
230
231
|
description: "Writes or updates tests for a change and runs them",
|
|
231
|
-
tools: ["read", "write", "edit", "grep", "find", "ls", "bash"],
|
|
232
|
+
tools: ["read", "write", "edit", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
232
233
|
source: "builtin",
|
|
233
234
|
systemPrompt: `You are a test engineer. You add or update tests for a change and run them.
|
|
234
235
|
|
|
@@ -260,7 +261,7 @@ What is still untested and why.`,
|
|
|
260
261
|
{
|
|
261
262
|
name: "debugger",
|
|
262
263
|
description: "Root-cause analysis for a failing test or bug, read-only plus running tests",
|
|
263
|
-
tools: ["read", "grep", "find", "ls", "bash"],
|
|
264
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
264
265
|
source: "builtin",
|
|
265
266
|
systemPrompt: `You are a debugging specialist. You find the root cause of a failure from evidence, not guesses.
|
|
266
267
|
|
|
@@ -295,7 +296,7 @@ What else the bug or the fix could affect.`,
|
|
|
295
296
|
{
|
|
296
297
|
name: "documenter",
|
|
297
298
|
description: "Updates docs, READMEs, and changelog entries to match the code",
|
|
298
|
-
tools: ["read", "write", "edit", "grep", "find", "ls", "bash"],
|
|
299
|
+
tools: ["read", "write", "edit", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
299
300
|
source: "builtin",
|
|
300
301
|
systemPrompt: `You are a documentation specialist. You keep docs accurate and consistent with the code.
|
|
301
302
|
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* import for invocation concerns.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { WORKSPACE_READERS } from "../../lib/policy.ts";
|
|
10
|
+
import { WEB_FETCH_TOOL, WEB_SEARCH_TOOL } from "../../lib/tool-names.ts";
|
|
9
11
|
import type { AgentConfig } from "./agents.ts";
|
|
10
12
|
import type { DispatchDefaults } from "./types.ts";
|
|
11
13
|
|
|
12
14
|
export { getPiInvocation, type PiInvocation } from "../../lib/pi-invocation.ts";
|
|
13
15
|
|
|
14
|
-
/** Tools a read-only delegation may use: structured readers and web readers only. */
|
|
15
|
-
export const READ_ONLY_TOOLS = [
|
|
16
|
+
/** Tools a read-only delegation may use: structured readers, documents, and web readers only. */
|
|
17
|
+
export const READ_ONLY_TOOLS = [...WORKSPACE_READERS, WEB_SEARCH_TOOL, WEB_FETCH_TOOL] as const;
|
|
16
18
|
|
|
17
19
|
/** Appended to a read-only agent's system prompt; the `--tools` list is the real gate. */
|
|
18
20
|
export const READ_ONLY_NOTE =
|