@gavin-hu/my-pi-agent 0.18.0 → 0.20.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 +66 -52
- package/README.zh-CN.md +56 -46
- package/docs/extensions/README.md +34 -0
- package/docs/extensions/board.md +507 -0
- package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +774 -574
- 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 +561 -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} +62 -52
- package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
- package/{extensions/plan/README.md → docs/extensions/plan.md} +440 -322
- 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 +298 -0
- package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
- package/docs/extensions/team.md +254 -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} +260 -163
- package/{extensions/worktree/README.md → docs/extensions/worktree.md} +137 -79
- package/extensions/board/commands.ts +105 -0
- package/extensions/board/config.ts +36 -0
- package/extensions/board/format.ts +170 -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 +388 -0
- package/extensions/board/result.ts +108 -0
- package/extensions/board/rows.ts +48 -0
- package/extensions/board/schema.ts +90 -0
- package/extensions/board/seat.ts +28 -0
- package/extensions/board/snapshot.ts +42 -0
- package/extensions/board/tools.ts +444 -0
- package/extensions/board/transport/router.ts +91 -0
- package/extensions/board/transport/server.ts +138 -0
- package/extensions/board/types.ts +121 -0
- package/extensions/board/view/client.ts +51 -0
- package/extensions/board/view/dialog.ts +43 -0
- package/extensions/board/view/keys.ts +125 -0
- package/extensions/board/view/live.ts +231 -0
- package/extensions/board/view/names.ts +91 -0
- package/extensions/board/view/page.ts +118 -0
- package/extensions/board/view/render.ts +519 -0
- package/extensions/board/view/styles.ts +228 -0
- package/extensions/board/view/team.ts +327 -0
- 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 -8
- package/extensions/bytefly/footer/price/price.ts +30 -8
- package/extensions/bytefly/footer/register.ts +5 -5
- package/extensions/bytefly/footer/render/format.ts +18 -0
- package/extensions/bytefly/footer/render/lines.ts +23 -7
- package/extensions/bytefly/footer/render/segments.ts +117 -31
- package/extensions/bytefly/footer/types.ts +29 -5
- package/extensions/bytefly/footer/unpriced.ts +42 -17
- package/extensions/bytefly/panel/commands.ts +67 -7
- 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-runtime.ts +0 -6
- package/extensions/bytefly/panel/settings.ts +75 -43
- 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/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/check/commands.ts +2 -1
- package/extensions/context/tui.ts +1 -1
- package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
- package/extensions/file-browse/files/classify.ts +136 -0
- package/extensions/file-browse/files/files.ts +12 -0
- package/extensions/file-browse/files/index.ts +90 -0
- package/extensions/file-browse/files/list.ts +261 -0
- package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
- package/extensions/file-browse/files/tree.ts +101 -0
- 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 -17
- 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/view/emit.ts +51 -0
- package/extensions/file-browse/{html.ts → view/html.ts} +19 -18
- package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
- package/extensions/file-browse/{markdown.ts → view/markdown.ts} +18 -20
- package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +19 -14
- 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/job/runtime.ts +4 -4
- 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 +185 -52
- package/extensions/plan/format.ts +10 -3
- package/extensions/plan/index.ts +23 -5
- package/extensions/plan/plan-files.ts +88 -0
- package/extensions/plan/plan-index.ts +43 -9
- package/extensions/plan/plan-text.ts +33 -23
- package/extensions/plan/plans.ts +27 -34
- package/extensions/plan/policy.ts +30 -17
- package/extensions/plan/review.ts +3 -27
- package/extensions/plan/runtime.ts +52 -11
- package/extensions/plan/sections.ts +112 -0
- package/extensions/plan/steps.ts +3 -22
- package/extensions/plan/todos.ts +51 -3
- package/extensions/plan/tools/exit.ts +20 -95
- package/extensions/plan/tools/todo-seed.ts +4 -2
- package/extensions/plan/tools/write.ts +12 -1
- package/extensions/plan/tui.ts +5 -34
- package/extensions/plan/types.ts +15 -8
- package/extensions/plan/web-blocks.ts +89 -0
- package/extensions/plan/web-chrome.ts +3 -5
- package/extensions/plan/web-client.ts +179 -330
- package/extensions/plan/web-collab.ts +3 -3
- package/extensions/plan/web-comments.ts +18 -8
- package/extensions/plan/web-composer.ts +80 -0
- package/extensions/plan/web-draft.ts +40 -0
- package/extensions/plan/web-keys.ts +134 -0
- package/extensions/plan/web-outline.ts +192 -0
- package/extensions/plan/web-page.ts +4 -8
- package/extensions/plan/web-plan.ts +150 -0
- package/extensions/plan/web-router.ts +3 -58
- package/extensions/plan/web-settle.ts +194 -0
- 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} +15 -14
- 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 +11 -5
- package/extensions/session-bus/runtime.ts +220 -267
- package/extensions/session-bus/store.ts +55 -7
- package/extensions/session-bus/tools.ts +31 -66
- package/extensions/session-bus/tui.ts +5 -5
- package/extensions/session-bus/types.ts +23 -14
- package/extensions/subagent/agents.ts +9 -8
- package/extensions/subagent/commands.ts +2 -1
- package/extensions/subagent/invocation.ts +4 -2
- package/extensions/team/commands.ts +145 -0
- package/extensions/team/dispatch.ts +133 -0
- package/extensions/team/format.ts +119 -0
- package/extensions/team/index.ts +34 -0
- package/extensions/team/schema.ts +29 -0
- package/extensions/team/tools.ts +267 -0
- package/extensions/team/types.ts +65 -0
- package/extensions/todo/index.ts +4 -1
- package/extensions/todo/nudge.ts +8 -2
- 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 +106 -251
- 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/intake.ts +245 -0
- 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/commands.ts +24 -17
- package/extensions/worktree/exit.ts +12 -6
- package/extensions/worktree/git.ts +22 -14
- package/extensions/worktree/guard.ts +18 -290
- 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 +11 -8
- package/lib/board/card.ts +479 -0
- package/lib/board/links.ts +141 -0
- package/lib/board/page-pointer.ts +310 -0
- package/lib/board/round-text.ts +81 -0
- package/lib/board/round.ts +94 -0
- package/lib/board/rules.ts +437 -0
- package/lib/board/session.ts +105 -0
- package/lib/board/store.ts +497 -0
- package/lib/board/view.ts +234 -0
- package/lib/errors.ts +12 -4
- package/lib/format.ts +35 -0
- package/lib/http/guard.ts +112 -0
- package/lib/http/index.ts +16 -5
- package/lib/http/serve.ts +237 -0
- package/lib/http/server.ts +12 -10
- package/lib/http/sink.ts +135 -0
- package/lib/http/token.ts +38 -0
- package/lib/plan-entry.ts +11 -0
- package/lib/policy.ts +25 -2
- package/lib/requirements.ts +6 -7
- package/lib/session-label.ts +64 -0
- package/lib/session-roster.ts +201 -0
- package/lib/session-tool.ts +184 -0
- package/lib/spawn.ts +37 -3
- package/lib/stray-fields.ts +45 -0
- package/lib/tool-names.ts +38 -2
- package/lib/tui/glyphs.ts +3 -2
- package/lib/web/README.md +57 -4
- package/lib/web/client-runtime.ts +123 -0
- package/lib/web/components.ts +5 -0
- package/lib/web/index.ts +4 -2
- package/lib/web/markup.ts +88 -1
- package/lib/web/names.ts +42 -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/bytefly/panel/tui/extensions-view.ts +0 -40
- package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
- package/extensions/file-browse/README.md +0 -506
- package/extensions/file-browse/files.ts +0 -564
- package/extensions/file-browse/server.ts +0 -141
- package/extensions/job/process.ts +0 -35
- 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
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claim overlap (pure).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The bus's one answer to "do these paths collide?": the runtime checks it before
|
|
5
|
+
* a claim is written, and the store checks it when a claim is read back. Two
|
|
6
|
+
* copies would let a claim succeed here and read back as a conflict there, which
|
|
7
|
+
* teaches the user that a claim is arbitrary.
|
|
8
|
+
*
|
|
9
|
+
* One export. `normalizeClaimPath` is the predicate's own step — a spellings rule
|
|
10
|
+
* with a single caller, inside this module — so a caller cannot normalize a path
|
|
11
|
+
* one way and compare it another.
|
|
9
12
|
*
|
|
10
13
|
* A claim on a directory covers every file below it, in either direction, and a
|
|
11
|
-
* claim is always a repository-relative, posix-separated path:
|
|
12
|
-
*
|
|
14
|
+
* claim is always a repository-relative, posix-separated path: both sides are
|
|
15
|
+
* normalized here, because a Windows spelling (`a\b`) and a posix one (`a/b`)
|
|
13
16
|
* naming the same file must not read as two claims.
|
|
14
17
|
*/
|
|
15
18
|
|
|
16
19
|
/** A repository-relative path with one separator and no trailing slash. */
|
|
17
|
-
|
|
20
|
+
function normalizeClaimPath(raw: string): string {
|
|
18
21
|
const posix = raw.trim().replace(/\\/g, "/");
|
|
19
22
|
const trimmed = posix
|
|
20
23
|
.replace(/^\.\//, "")
|
|
@@ -31,13 +34,3 @@ export function claimsConflict(a: string, b: string): boolean {
|
|
|
31
34
|
if (left === "" || right === "") return true;
|
|
32
35
|
return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
/** The first two prefixes in `paths` that collide, if any. */
|
|
36
|
-
export function findClaimOverlap(paths: readonly string[]): [string, string] | undefined {
|
|
37
|
-
for (let i = 0; i < paths.length; i++) {
|
|
38
|
-
for (let j = i + 1; j < paths.length; j++) {
|
|
39
|
-
if (claimsConflict(paths[i], paths[j])) return [paths[i], paths[j]];
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
@@ -6,63 +6,41 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
9
|
-
import {
|
|
9
|
+
import { MAX_MESSAGE_TEXT } from "../../lib/session-tool.ts";
|
|
10
|
+
import { SESSION_TOOL } from "../../lib/tool-names.ts";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The label rules live in `lib/` because a label is shared vocabulary: the board
|
|
14
|
+
* records one on a card it claims, and `session-bus` settles the one it is
|
|
15
|
+
* addressed by. Re-exported here so every module in this extension keeps
|
|
16
|
+
* importing its rules from the protocol they belong to.
|
|
17
|
+
*/
|
|
18
|
+
export { defaultLabel, MAX_LABEL, normalizeLabel, shortId } from "../../lib/session-label.ts";
|
|
19
|
+
|
|
20
|
+
export const TOOL_NAME = SESSION_TOOL;
|
|
12
21
|
export const COMMAND_NAME = "sessions";
|
|
13
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The text cap, from `lib/session-tool.ts`, where the callers that clip to it live.
|
|
24
|
+
* Re-exported the way the label rules are, so this extension imports its protocol's
|
|
25
|
+
* constants from one place and two extensions cannot drift apart on the number.
|
|
26
|
+
*/
|
|
27
|
+
export const MAX_TEXT = MAX_MESSAGE_TEXT;
|
|
14
28
|
export const MAX_PATHS = 20;
|
|
15
|
-
export const MAX_LABEL = 40;
|
|
16
29
|
export const DEFAULT_LIMIT = 20;
|
|
17
30
|
export const MAX_LIMIT = 50;
|
|
18
31
|
|
|
19
32
|
export type SessionMode = "tui" | "rpc" | "json" | "print" | "unknown";
|
|
20
|
-
export type SessionAction = "status" | "send" | "inbox" | "claim" | "release" | "join"
|
|
33
|
+
export type SessionAction = "status" | "send" | "inbox" | "claim" | "release" | "join";
|
|
21
34
|
export type MessageKind = "message" | "event" | "ask" | "handoff";
|
|
22
35
|
|
|
23
|
-
const ACTIONS: readonly SessionAction[] = [
|
|
24
|
-
"status",
|
|
25
|
-
"send",
|
|
26
|
-
"inbox",
|
|
27
|
-
"claim",
|
|
28
|
-
"release",
|
|
29
|
-
"join",
|
|
30
|
-
"leave",
|
|
31
|
-
"watch",
|
|
32
|
-
"name",
|
|
33
|
-
];
|
|
36
|
+
const ACTIONS: readonly SessionAction[] = ["status", "send", "inbox", "claim", "release", "join"];
|
|
34
37
|
const KINDS: readonly MessageKind[] = ["message", "event", "ask", "handoff"];
|
|
35
38
|
|
|
36
|
-
/** Only a terminal session is visible on the bus before it asks to join. */
|
|
37
|
-
export function isAutoJoinMode(mode: string | undefined): boolean {
|
|
38
|
-
return mode === "tui";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
39
|
/** The session mode as the record stores it. */
|
|
42
40
|
export function sessionMode(mode: string | undefined): SessionMode {
|
|
43
41
|
return mode === "tui" || mode === "rpc" || mode === "json" || mode === "print" ? mode : "unknown";
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
/** A safe label, used both for display and as a target. */
|
|
47
|
-
export function normalizeLabel(raw: string | undefined, fallback: string): string {
|
|
48
|
-
const text = sanitizeLine(raw ?? "")
|
|
49
|
-
.replace(/\s+/g, " ")
|
|
50
|
-
.trim();
|
|
51
|
-
return (text || fallback).slice(0, MAX_LABEL);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** A short, display-only suffix for a session id. */
|
|
55
|
-
export function shortId(id: string): string {
|
|
56
|
-
const clean = id.replace(/[^a-zA-Z0-9]/g, "");
|
|
57
|
-
return (clean || id).slice(-6);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** The automatic label before uniqueness is applied. */
|
|
61
|
-
export function defaultLabel(sessionName: string | undefined, cwd: string, id: string): string {
|
|
62
|
-
const base = normalizeLabel(sessionName, normalizeLabel(cwd.split(/[\\/]/).filter(Boolean).at(-1), "session"));
|
|
63
|
-
return normalizeLabel(base, `session-${shortId(id)}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
44
|
/** An action, or undefined when the model sent an unknown one. */
|
|
67
45
|
export function normalizeAction(raw: unknown): SessionAction | undefined {
|
|
68
46
|
return typeof raw === "string" && ACTIONS.includes(raw as SessionAction) ? (raw as SessionAction) : undefined;
|
|
@@ -105,14 +83,6 @@ export function normalizePaths(repoRoot: string, cwd: string, raw: unknown): { p
|
|
|
105
83
|
return { paths };
|
|
106
84
|
}
|
|
107
85
|
|
|
108
|
-
/**
|
|
109
|
-
* Whether one claim covers the other path in either direction.
|
|
110
|
-
*
|
|
111
|
-
* The predicate is `lib/claims/overlap.ts`, because plan approval validates a
|
|
112
|
-
* split with the same question; a second copy here is how the two answers drift.
|
|
113
|
-
*/
|
|
114
|
-
export { claimsConflict } from "../../lib/claims/overlap.ts";
|
|
115
|
-
|
|
116
86
|
/** Resolve `to` to active sessions; `*` is broadcast and excludes the sender. */
|
|
117
87
|
export function resolveRecipients(
|
|
118
88
|
sessions: readonly { id: string; label: string }[],
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A claim path as the store keeps it: repository-relative, posix, canonical.
|
|
3
|
+
*
|
|
4
|
+
* A path is data — it is resolved against the repository root, and canonicalised
|
|
5
|
+
* through its real target before it is compared or stored — so a symlinked
|
|
6
|
+
* spelling inside the repository and the file it names are one claim, and a path
|
|
7
|
+
* that leaves the repository is refused before anything is written.
|
|
8
|
+
*
|
|
9
|
+
* `lib/path.ts` owns the containment predicates; this owns the claim's shape.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { isAbsolute, join, relative, sep } from "node:path";
|
|
13
|
+
import { isInside, realPathOfNearest } from "../../lib/path.ts";
|
|
14
|
+
|
|
15
|
+
/** A path this module declined, with the reason a caller can print. */
|
|
16
|
+
export interface ClaimPathError {
|
|
17
|
+
/** The path as the caller passed it, so a refusal names what it read. */
|
|
18
|
+
path: string;
|
|
19
|
+
/** Why it cannot be a claim. */
|
|
20
|
+
reason: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Every path resolved and deduplicated, or the first one refused. */
|
|
24
|
+
export type ResolvedClaimPaths = { paths: string[] } | { error: ClaimPathError };
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The wording of the one refusal, shared so approval and the bus cannot phrase it
|
|
28
|
+
* two ways: the caller prefixes the subject it knows (a unit, or a session's call).
|
|
29
|
+
*/
|
|
30
|
+
export function claimPathMessage(error: ClaimPathError): string {
|
|
31
|
+
return `path "${error.path}" ${error.reason}.`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* What the root itself resolves to, which is never a claim: it would cover every
|
|
36
|
+
* file in the repository, so a unit that lists it owns the repository.
|
|
37
|
+
*/
|
|
38
|
+
const ROOT_ITSELF = "is the repository, not a path in it";
|
|
39
|
+
|
|
40
|
+
/** What a path outside the root resolves to. */
|
|
41
|
+
const OUTSIDE = "does not resolve to a file in the repository";
|
|
42
|
+
|
|
43
|
+
/** What an absolute path is refused for: it is not the claim's to place. */
|
|
44
|
+
const ABSOLUTE = "is absolute; a claim is relative to the repository root";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Resolve `paths` against `root`, or refuse the first one that is not in it.
|
|
48
|
+
*
|
|
49
|
+
* A path that does not exist yet is accepted: `realPathOfNearest` resolves the
|
|
50
|
+
* nearest existing ancestor, so a unit may name the directory its work creates.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveClaimPaths(root: string, paths: readonly string[]): ResolvedClaimPaths {
|
|
53
|
+
const realRoot = realPathOfNearest(root);
|
|
54
|
+
const canonical: string[] = [];
|
|
55
|
+
for (const path of paths) {
|
|
56
|
+
// `join` would re-root an absolute path under the repository, which is not what
|
|
57
|
+
// a caller meant: refuse it rather than claim a directory it never named.
|
|
58
|
+
if (isAbsolute(path)) return { error: { path, reason: ABSOLUTE } };
|
|
59
|
+
const target = realPathOfNearest(join(root, path));
|
|
60
|
+
const rel = relative(realRoot, target);
|
|
61
|
+
if (rel === "") return { error: { path, reason: ROOT_ITSELF } };
|
|
62
|
+
if (!isInside(realRoot, target)) return { error: { path, reason: OUTSIDE } };
|
|
63
|
+
const normalized = rel.split(sep).join("/");
|
|
64
|
+
if (!canonical.includes(normalized)) canonical.push(normalized);
|
|
65
|
+
}
|
|
66
|
+
return { paths: canonical };
|
|
67
|
+
}
|
|
@@ -97,21 +97,7 @@ export function resultSections(details: SessionDetails): TailResultInput<BusRow>
|
|
|
97
97
|
],
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
-
case "
|
|
101
|
-
const conflicts = details.conflicts ?? [];
|
|
102
|
-
const claimed = details.claimed ?? [];
|
|
103
|
-
const rows: BusRow[] = [
|
|
104
|
-
...conflicts.map((claim) => ({ kind: "claim" as const, claim })),
|
|
105
|
-
...claimed.map((label) => ({ kind: "result" as const, label, tone: "ok" as const })),
|
|
106
|
-
];
|
|
107
|
-
if (rows.length === 0) return undefined;
|
|
108
|
-
return {
|
|
109
|
-
kind: "sections",
|
|
110
|
-
sections: [section(rows, conflicts.length > 0 ? "Conflicts" : "Claimed")],
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
case "release":
|
|
114
|
-
case "leave": {
|
|
100
|
+
case "release": {
|
|
115
101
|
const released = details.released ?? [];
|
|
116
102
|
const notOwned = details.notOwned ?? [];
|
|
117
103
|
const sections: TailSection<BusRow>[] = [];
|
|
@@ -141,9 +127,7 @@ export function resultSections(details: SessionDetails): TailResultInput<BusRow>
|
|
|
141
127
|
sections: [section(delivered.map((label) => ({ kind: "result", label, tone: "ok" })))],
|
|
142
128
|
};
|
|
143
129
|
}
|
|
144
|
-
case "
|
|
145
|
-
return undefined;
|
|
146
|
-
case "name":
|
|
130
|
+
case "join":
|
|
147
131
|
return undefined;
|
|
148
132
|
}
|
|
149
133
|
}
|
|
@@ -164,11 +148,14 @@ export function collapsedLine(theme: Theme, details: SessionDetails): string {
|
|
|
164
148
|
const cells = [count(board.length, "session")];
|
|
165
149
|
if (unread > 0) cells.push(`${unread} unread`);
|
|
166
150
|
cells.push(count(claims, "claim"));
|
|
167
|
-
const units = details.status?.units;
|
|
168
|
-
if (units) cells.push(`${units.joined}/${units.total} units`);
|
|
169
151
|
const watching = details.status?.watching;
|
|
170
152
|
if (watching) cells.push(watching.wake ? "watch wake" : "watch on");
|
|
171
|
-
|
|
153
|
+
const line = paint(theme, "meta", cells.join(sep));
|
|
154
|
+
// A reader that has not joined is looking at a bus it is not on.
|
|
155
|
+
if (details.status?.onBus === false) {
|
|
156
|
+
return `${line}${paint(theme, "faint", sep)}${paint(theme, "warn", "not on the bus \u2014 session join puts you on it")}`;
|
|
157
|
+
}
|
|
158
|
+
return line;
|
|
172
159
|
}
|
|
173
160
|
case "send": {
|
|
174
161
|
const delivered = details.delivered ?? [];
|
|
@@ -198,26 +185,7 @@ export function collapsedLine(theme: Theme, details: SessionDetails): string {
|
|
|
198
185
|
if (claimed.length === 0) return paint(theme, "meta", "Claimed nothing");
|
|
199
186
|
return paint(theme, "ok", `Claimed ${claimed.map((path) => sanitizeLine(path)).join(", ")}`);
|
|
200
187
|
}
|
|
201
|
-
case "
|
|
202
|
-
const conflicts = details.conflicts ?? [];
|
|
203
|
-
const where = [details.team, details.unit]
|
|
204
|
-
.filter(Boolean)
|
|
205
|
-
.map((value) => sanitizeLine(value as string))
|
|
206
|
-
.join("/");
|
|
207
|
-
if (conflicts.length > 0) {
|
|
208
|
-
const [first] = conflicts;
|
|
209
|
-
const owner = first ? sanitizeLine(first.ownerLabel) : "a peer";
|
|
210
|
-
return paint(
|
|
211
|
-
theme,
|
|
212
|
-
"warn",
|
|
213
|
-
`Could not join ${where}${sep}${count(conflicts.length, "conflict")} held by ${owner}`,
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
const claimed = details.claimed ?? [];
|
|
217
|
-
return paint(theme, "ok", `Joined ${where}${sep}claimed ${claimed.map((path) => sanitizeLine(path)).join(", ")}`);
|
|
218
|
-
}
|
|
219
|
-
case "release":
|
|
220
|
-
case "leave": {
|
|
188
|
+
case "release": {
|
|
221
189
|
const released = details.released ?? [];
|
|
222
190
|
const notOwned = details.notOwned ?? [];
|
|
223
191
|
const parts: string[] = [];
|
|
@@ -228,16 +196,13 @@ export function collapsedLine(theme: Theme, details: SessionDetails): string {
|
|
|
228
196
|
}
|
|
229
197
|
return parts.length > 0 ? parts.join(paint(theme, "faint", sep)) : paint(theme, "meta", "Nothing to release");
|
|
230
198
|
}
|
|
231
|
-
case "
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
);
|
|
199
|
+
case "join": {
|
|
200
|
+
const label = paint(theme, "ok", sanitizeLine(details.label ?? ""));
|
|
201
|
+
const state =
|
|
202
|
+
details.watching?.armed !== true
|
|
203
|
+
? paint(theme, "meta", "not listening")
|
|
204
|
+
: paint(theme, "meta", details.watching.wake === true ? "listening, wake" : "listening");
|
|
205
|
+
return `${label}${paint(theme, "faint", sep)}${state}`;
|
|
239
206
|
}
|
|
240
|
-
case "name":
|
|
241
|
-
return paint(theme, "ok", `Named this session "${sanitizeLine(details.label ?? "")}"`);
|
|
242
207
|
}
|
|
243
208
|
}
|
|
@@ -14,7 +14,8 @@ import { GLYPHS, SEPARATORS } from "../../lib/tui/glyphs.ts";
|
|
|
14
14
|
import { listRow, statusRow } from "../../lib/tui/rows.ts";
|
|
15
15
|
import type { TailRowLine } from "../../lib/tui/tool.ts";
|
|
16
16
|
import { paint } from "../../lib/tui/tokens.ts";
|
|
17
|
-
import {
|
|
17
|
+
import { idSuffix } from "./format.ts";
|
|
18
|
+
import type { RosterState } from "../../lib/session-roster.ts";
|
|
18
19
|
import type { BusMessage, ClaimRecord, SessionRecord, SessionStatus } from "./types.ts";
|
|
19
20
|
|
|
20
21
|
/** Claim paths the focus block lists before it points at the pane. */
|
|
@@ -27,7 +28,7 @@ export function count(n: number, noun: string): string {
|
|
|
27
28
|
|
|
28
29
|
/** One session on the board, with the claims it holds. */
|
|
29
30
|
export interface SessionRow {
|
|
30
|
-
session: SessionRecord & { unread: number };
|
|
31
|
+
session: SessionRecord & { unread: number; state: RosterState };
|
|
31
32
|
/** Whether this row is the session reading the board. */
|
|
32
33
|
self: boolean;
|
|
33
34
|
claims: ClaimRecord[];
|
|
@@ -69,7 +70,7 @@ export function sessionRowLine(
|
|
|
69
70
|
options: { selected?: boolean; marker?: boolean } = {},
|
|
70
71
|
): string {
|
|
71
72
|
const label = `${sanitizeLine(row.session.label)}${row.self ? " · you" : ""}`;
|
|
72
|
-
const counts = [`${row.session.unread} unread`];
|
|
73
|
+
const counts = [row.session.state === "gone" ? "gone" : `${row.session.unread} unread`];
|
|
73
74
|
if (row.claims.length > 0) counts.push(count(row.claims.length, "claim"));
|
|
74
75
|
return listRow(theme, width, {
|
|
75
76
|
selected: options.selected === true,
|
|
@@ -143,13 +144,17 @@ export function summaryCells(
|
|
|
143
144
|
theme: Theme,
|
|
144
145
|
rows: readonly SessionRow[],
|
|
145
146
|
watching: { armed: boolean; wake: boolean } | undefined,
|
|
147
|
+
onBus = true,
|
|
146
148
|
): string[] {
|
|
147
149
|
const unread = rows.reduce((total, row) => total + row.session.unread, 0);
|
|
148
150
|
const claims = rows.reduce((total, row) => total + row.claims.length, 0);
|
|
151
|
+
const gone = rows.filter((row) => row.session.state === "gone").length;
|
|
149
152
|
const cells = [paint(theme, "meta", count(rows.length, "session"))];
|
|
153
|
+
if (gone > 0) cells.push(paint(theme, "bad", `${gone} gone`));
|
|
150
154
|
if (unread > 0) cells.push(paint(theme, "label", `${unread} unread`));
|
|
151
155
|
cells.push(paint(theme, "meta", count(claims, "claim")));
|
|
152
156
|
if (watching) cells.push(paint(theme, "ok", watching.wake ? "watch wake" : "watch on"));
|
|
157
|
+
if (!onBus) cells.push(paint(theme, "warn", "not on the bus"));
|
|
153
158
|
return cells;
|
|
154
159
|
}
|
|
155
160
|
|
|
@@ -162,8 +167,9 @@ export function sessionDetailBlock(row: SessionRow, now: number): string[] {
|
|
|
162
167
|
lines.push(`claims: ${shown.join(", ")}${hidden > 0 ? ` +${hidden} more` : ""}`);
|
|
163
168
|
}
|
|
164
169
|
lines.push(
|
|
165
|
-
`mode: ${sanitizeLine(row.session.mode)}${SEPARATORS.item}id: ${
|
|
166
|
-
`${SEPARATORS.item}last seen: ${formatDuration(Math.max(0, now - row.session.lastSeen))} ago
|
|
170
|
+
`mode: ${sanitizeLine(row.session.mode)}${SEPARATORS.item}id: ${idSuffix(row.session.id)}` +
|
|
171
|
+
`${SEPARATORS.item}last seen: ${formatDuration(Math.max(0, now - row.session.lastSeen))} ago` +
|
|
172
|
+
`${row.session.state === "gone" ? `${SEPARATORS.item}process gone` : ""}`,
|
|
167
173
|
);
|
|
168
174
|
return lines;
|
|
169
175
|
}
|