pi-crew 0.11.0 → 0.11.2
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/CHANGELOG.md +155 -9
- package/README.md +161 -1037
- package/agents/verifier.md +18 -7
- package/dist/index.mjs +744 -90644
- package/docs/README.md +57 -46
- package/docs/architecture.md +87 -33
- package/docs/commands-reference.md +9 -5
- package/docs/troubleshooting.md +3 -2
- package/package.json +1 -3
- package/schema.json +39 -0
- package/skills/real-test-pi-crew/REPORT-TEMPLATE.md +2 -0
- package/skills/real-test-pi-crew/SKILL.md +371 -34
- package/src/agents/agent-config.ts +1 -1
- package/src/agents/discover-agents.ts +1 -1
- package/src/config/config-validation.ts +15 -2
- package/src/config/config.ts +47 -13
- package/src/config/defaults.ts +0 -1
- package/src/config/env-vars.ts +35 -0
- package/src/config/types.ts +19 -5
- package/src/errors.ts +2 -2
- package/src/extension/async-notifier.ts +23 -0
- package/src/extension/crew-vibes/config.ts +0 -21
- package/src/extension/crew-vibes/index.ts +0 -2
- package/src/extension/crew-vibes/render.ts +1 -50
- package/src/extension/help.ts +21 -12
- package/src/extension/knowledge-injection.ts +2 -1
- package/src/extension/management.ts +8 -3
- package/src/extension/notification-sink.ts +17 -0
- package/src/extension/register.ts +7 -2
- package/src/extension/registration/command-utils.ts +28 -2
- package/src/extension/registration/commands/dashboard.ts +11 -1
- package/src/extension/registration/commands/manage.ts +36 -19
- package/src/extension/registration/commands/run.ts +24 -2
- package/src/extension/registration/commands/shared.ts +23 -1
- package/src/extension/registration/commands/status.ts +25 -2
- package/src/extension/registration/context-builder.ts +8 -2
- package/src/extension/registration/health-notify-policy.ts +100 -0
- package/src/extension/registration/lazy-configurers.ts +35 -0
- package/src/extension/registration/lifecycle-handlers.ts +91 -30
- package/src/extension/registration/lifecycle.ts +75 -10
- package/src/extension/registration/observability.ts +98 -35
- package/src/extension/registration/registration-types.ts +7 -5
- package/src/extension/registration/runtime-cleanup.ts +9 -3
- package/src/extension/registration/subagent-helpers.ts +38 -0
- package/src/extension/registration/subagent-tools.ts +16 -6
- package/src/extension/registration/team-tool.ts +10 -3
- package/src/extension/registration/terminal-status-wiring.ts +172 -0
- package/src/extension/registration/viewers.ts +6 -0
- package/src/extension/registration/wire-cross-extension.ts +28 -0
- package/src/extension/run-compare.ts +220 -0
- package/src/extension/run-export.ts +37 -5
- package/src/extension/run-maintenance.ts +155 -5
- package/src/extension/team-tool/dispatch/index.ts +3 -2
- package/src/extension/team-tool/dispatch/manage.ts +5 -2
- package/src/extension/team-tool/goal.ts +4 -1
- package/src/extension/team-tool/handle-settings.ts +33 -4
- package/src/extension/team-tool/health-monitor.ts +21 -7
- package/src/extension/team-tool/lifecycle-actions.ts +49 -1
- package/src/extension/team-tool/plan.ts +10 -0
- package/src/extension/team-tool/routing-hint.ts +63 -0
- package/src/extension/team-tool/status.ts +4 -0
- package/src/extension/team-tool.ts +52 -6
- package/src/extension/webhook-notify.ts +382 -0
- package/src/observability/metric-sink.ts +12 -2
- package/src/prompt/prompt-runtime.ts +82 -31
- package/src/prompt/worker-events-channel.ts +12 -0
- package/src/runtime/README.md +1 -1
- package/src/runtime/async-runner.ts +87 -1
- package/src/runtime/background-runner.ts +313 -234
- package/src/runtime/broker/crew-broker.ts +17 -11
- package/src/runtime/broker/delegate/shadow-lifecycle.ts +92 -0
- package/src/runtime/broker/wait-status-cache.ts +1 -1
- package/src/runtime/child-pi/child-pi-timers.ts +1 -1
- package/src/runtime/child-pi/mock-fixtures.ts +48 -0
- package/src/runtime/crew-agent-records.ts +337 -45
- package/src/runtime/deadletter.ts +43 -1
- package/src/runtime/delegate-spawn.ts +5 -1
- package/src/runtime/dispatch-batch.ts +72 -5
- package/src/runtime/goal-workflow/goal-loop-runner.ts +73 -4
- package/src/runtime/heartbeat/heartbeat-watcher.ts +7 -0
- package/src/runtime/model/model-fallback.ts +21 -1
- package/src/runtime/model/pi-args.ts +8 -10
- package/src/runtime/recovery/crash-recovery.ts +25 -1
- package/src/runtime/run-worker.ts +12 -1
- package/src/runtime/scheduling/global-worker-cap.ts +13 -6
- package/src/runtime/scheduling/run-coalesced-task-group.ts +27 -1
- package/src/runtime/scheduling/scheduler.ts +49 -13
- package/src/runtime/scheduling/semaphore.ts +148 -20
- package/src/runtime/scratchpad/README.md +1 -1
- package/src/runtime/scratchpad/protocol.ts +1 -1
- package/src/runtime/settings-store.ts +1 -1
- package/src/runtime/skill-instructions.ts +22 -0
- package/src/runtime/stale-reconciler.ts +85 -13
- package/src/runtime/task-display.ts +1 -1
- package/src/runtime/task-runner/pre-execution.ts +26 -2
- package/src/runtime/task-runner/prompt-builder.ts +142 -45
- package/src/runtime/task-runner.ts +21 -1
- package/src/runtime/team-runner.ts +38 -1
- package/src/runtime/workspace-lock.ts +4 -1
- package/src/schema/config-schema.ts +18 -0
- package/src/schema/team-tool-schema.ts +17 -0
- package/src/state/atomic-write.ts +53 -0
- package/src/state/contracts.ts +109 -0
- package/src/state/coordination/locks.ts +191 -33
- package/src/state/coordination/mailbox.ts +140 -15
- package/src/state/crew-init.ts +87 -12
- package/src/state/event-log/cursor.ts +37 -1
- package/src/state/event-log/event-log-rotation.ts +72 -7
- package/src/state/stores/active-run-registry.ts +13 -1
- package/src/state/stores/state-store.ts +112 -22
- package/src/state/types.ts +4 -0
- package/src/ui/adaptive-card.ts +65 -0
- package/src/ui/agents-jobs-browser.ts +70 -64
- package/src/ui/card-colors.ts +36 -7
- package/src/ui/dashboard-panes/agents-pane.ts +55 -14
- package/src/ui/dashboard-panes/cancellation-pane.ts +0 -42
- package/src/ui/dashboard-panes/health-pane.ts +7 -5
- package/src/ui/dashboard-panes/mailbox-pane.ts +22 -6
- package/src/ui/dashboard-panes/metrics-pane.ts +15 -7
- package/src/ui/dashboard-panes/pane-theme.ts +21 -0
- package/src/ui/dashboard-panes/plan-pane.ts +63 -30
- package/src/ui/dashboard-panes/progress-pane.ts +3 -2
- package/src/ui/dashboard-panes/schedules-pane.ts +44 -21
- package/src/ui/dashboard-panes/transcript-pane.ts +11 -5
- package/src/ui/dwf-phase-display.ts +3 -20
- package/src/ui/format-helpers.ts +22 -0
- package/src/ui/heartbeat-aggregator.ts +34 -0
- package/src/ui/inline-panel/crew-editor.ts +13 -3
- package/src/ui/inline-panel/index.ts +60 -4
- package/src/ui/keybinding-map.ts +251 -35
- package/src/ui/live-conversation-overlay.ts +180 -47
- package/src/ui/live-run-sidebar.ts +134 -55
- package/src/ui/mascot.ts +32 -16
- package/src/ui/overlays/agent-picker-overlay.ts +81 -26
- package/src/ui/overlays/confirm-overlay.ts +55 -29
- package/src/ui/overlays/help-overlay.ts +108 -53
- package/src/ui/overlays/mailbox-compose-overlay.ts +89 -50
- package/src/ui/overlays/mailbox-detail-overlay.ts +137 -57
- package/src/ui/powerbar-publisher.ts +0 -1
- package/src/ui/rail.ts +333 -0
- package/src/ui/run-dashboard.ts +193 -79
- package/src/ui/run-snapshot-cache.ts +18 -1
- package/src/ui/settings-overlay.ts +81 -39
- package/src/ui/spinner.ts +26 -2
- package/src/ui/terminal-status.ts +7 -1
- package/src/ui/theme-adapter.ts +0 -45
- package/src/ui/theme-discovery.ts +12 -6
- package/src/ui/tool-progress-formatter.ts +128 -9
- package/src/ui/tool-renderers/brief-mode.ts +10 -67
- package/src/ui/tool-renderers/index.ts +374 -523
- package/src/ui/transcript-viewer.ts +30 -12
- package/src/ui/widget/index.ts +32 -52
- package/src/ui/widget/task-list.ts +64 -32
- package/src/ui/widget/widget-formatters.ts +3 -402
- package/src/ui/widget/widget-model.ts +28 -7
- package/src/ui/widget/widget-renderer.ts +201 -128
- package/src/ui/widget/widget-types.ts +0 -2
- package/src/utils/incremental-reader.ts +11 -3
- package/src/utils/paths.ts +94 -12
- package/src/utils/project-markers.ts +40 -0
- package/src/utils/visual.ts +0 -4
- package/src/worktree/worktree-manager.ts +206 -26
- package/workflows/distill.workflow.md +3 -3
- package/workflows/fast-fix.workflow.md +1 -1
- package/workflows/plan-execute.workflow.md +1 -1
- package/workflows/review.workflow.md +1 -1
- package/workflows/strict-fast-fix.workflow.md +1 -1
- package/docs/migration-v0.4-v0.5.md +0 -208
- package/docs/runtime-flow.md +0 -148
- package/src/extension/crew-vibes/figures.ts +0 -22
- package/src/extension/crew-vibes/font-detect.ts +0 -71
- package/src/ui/dynamic-border.ts +0 -35
- package/src/ui/loaders.ts +0 -6
- package/src/ui/overlay-stack.ts +0 -148
|
@@ -1,6 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mailbox compose overlay — RAIL design language (M4/E1, 2026-09-16).
|
|
3
|
+
*
|
|
4
|
+
* ┏ COMPOSE ▸ mailbox
|
|
5
|
+
* ┃ › from: operator │ Preview
|
|
6
|
+
* ┃ › to: leader │ # Title
|
|
7
|
+
* ┃ › body: hello │ • item
|
|
8
|
+
* ┃ › taskId: │
|
|
9
|
+
* ┃ › [ ] Send to outbox │
|
|
10
|
+
* ┗ P close preview · Tab cycle · Enter submit · Esc discard
|
|
11
|
+
*
|
|
12
|
+
* Frame: canopy + `┃` body + `┗` close cap. When the preview is on, the body is
|
|
13
|
+
* a genuine two-column layout and `│` is the inner COLUMN separator (§2.D); the
|
|
14
|
+
* canopy/hint rows are NOT part of the split any more, so the hint keeps its
|
|
15
|
+
* full width instead of being cropped to 60% of the overlay.
|
|
16
|
+
*
|
|
17
|
+
* Keys/behaviour are unchanged (same `overlay:*` dispatch, same free-text
|
|
18
|
+
* passthrough, same validation errors, same discard confirmation). Hints come
|
|
19
|
+
* from `formatHint` (discard LAST, keys through `keyToken`).
|
|
20
|
+
*/
|
|
21
|
+
|
|
1
22
|
import type { MailboxDirection } from "../../state/coordination/mailbox.ts";
|
|
2
|
-
import { pad, truncate } from "../../utils/visual.ts";
|
|
3
|
-
import {
|
|
23
|
+
import { pad, sanitizeLine, truncate } from "../../utils/visual.ts";
|
|
24
|
+
import { overlayActionForKey } from "../keybinding-map.ts";
|
|
25
|
+
import { CURSOR, canopyLine, formatHint, RAIL, railLine, railRaw } from "../rail.ts";
|
|
4
26
|
import { asCrewTheme, type CrewTheme } from "../theme-adapter.ts";
|
|
5
27
|
import { ConfirmOverlay } from "./confirm-overlay.ts";
|
|
6
28
|
import { renderComposePreview } from "./mailbox-compose-preview.ts";
|
|
@@ -49,36 +71,53 @@ export class MailboxComposeOverlay {
|
|
|
49
71
|
|
|
50
72
|
render(width: number): string[] {
|
|
51
73
|
if (this.confirm) return this.confirm.render(width);
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
this.preview
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
if (width < 10) return [];
|
|
75
|
+
const theme = this.theme;
|
|
76
|
+
const budget = width - 2;
|
|
77
|
+
const hint = formatHint([
|
|
78
|
+
["P", this.preview ? "close preview" : "preview"],
|
|
79
|
+
["tab", "cycle"],
|
|
80
|
+
["enter", "submit"],
|
|
81
|
+
["escape", "discard"],
|
|
82
|
+
]);
|
|
83
|
+
const lines: string[] = [canopyLine({ word: "COMPOSE", subject: "mailbox", theme, budget })];
|
|
84
|
+
if (this.error) lines.push(railLine(RAIL.body, "error", truncate(this.error, budget), theme, budget));
|
|
85
|
+
|
|
86
|
+
const formBudget = this.preview ? Math.max(10, Math.floor(budget * 0.6)) : budget;
|
|
87
|
+
const previewBudget = Math.max(8, budget - formBudget - 3);
|
|
88
|
+
const formRows = [
|
|
89
|
+
this.fieldLine("from", formBudget),
|
|
90
|
+
this.fieldLine("to", formBudget),
|
|
91
|
+
this.fieldLine("body", formBudget),
|
|
92
|
+
this.fieldLine("taskId", formBudget),
|
|
93
|
+
`${this.activeField === 4 ? CURSOR : " "} [${this.fields.direction === "outbox" ? "x" : " "}] Send to outbox`,
|
|
65
94
|
];
|
|
66
|
-
if (!this.preview)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
95
|
+
if (!this.preview) {
|
|
96
|
+
for (const row of formRows) lines.push(railLine(RAIL.body, "border", truncate(row, budget), theme, budget));
|
|
97
|
+
} else {
|
|
98
|
+
const previewLines = renderComposePreview(this.fields.body, previewBudget, theme);
|
|
99
|
+
const max = Math.max(formRows.length, previewLines.length);
|
|
100
|
+
for (let index = 0; index < max; index += 1) {
|
|
101
|
+
const left = pad(truncate(formRows[index] ?? "", formBudget), formBudget);
|
|
102
|
+
const right = truncate(previewLines[index] ?? "", previewBudget);
|
|
103
|
+
// No separator when the preview column has no row at this height:
|
|
104
|
+
// a `│` with nothing to its right reads as a frame edge.
|
|
105
|
+
lines.push(railRaw(RAIL.body, "border", right ? `${left} ${theme.fg("dim", "│")} ${right}` : left, theme));
|
|
106
|
+
}
|
|
74
107
|
}
|
|
75
|
-
|
|
108
|
+
lines.push(railLine(RAIL.close, "border", theme.fg("dim", hint), theme, budget));
|
|
109
|
+
return lines;
|
|
76
110
|
}
|
|
77
111
|
|
|
78
112
|
private fieldLine(field: Exclude<FieldName, "direction">, width: number): string {
|
|
79
113
|
const active = FIELD_ORDER[this.activeField] === field;
|
|
80
114
|
const label = field === "taskId" ? "taskId" : field;
|
|
81
|
-
|
|
115
|
+
// `sanitizeLine`: a pasted / pre-filled value can carry a raw newline or
|
|
116
|
+
// TAB — printing it verbatim would break the rail row into several lines
|
|
117
|
+
// (the field is a single-line cell; multi-line content belongs to the
|
|
118
|
+
// preview column).
|
|
119
|
+
const value = sanitizeLine(this.fields[field] ?? "");
|
|
120
|
+
return `${active ? CURSOR : " "} ${label}: ${truncate(value, Math.max(8, width - label.length - 5))}`;
|
|
82
121
|
}
|
|
83
122
|
|
|
84
123
|
private activeName(): FieldName {
|
|
@@ -151,32 +190,32 @@ export class MailboxComposeOverlay {
|
|
|
151
190
|
this.confirm.handleInput(data);
|
|
152
191
|
return;
|
|
153
192
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
else this.activeField = (this.activeField + 1) % FIELD_ORDER.length;
|
|
178
|
-
return;
|
|
193
|
+
// M2-1: keys come from the central `overlay:*` keyspace (remappable via
|
|
194
|
+
// `.crew/config.json` → keybindings["overlay:mailbox-compose:<action>"]).
|
|
195
|
+
switch (overlayActionForKey("mailbox-compose", data)) {
|
|
196
|
+
case "cancel":
|
|
197
|
+
this.cancel();
|
|
198
|
+
return;
|
|
199
|
+
case "preview":
|
|
200
|
+
this.preview = !this.preview;
|
|
201
|
+
return;
|
|
202
|
+
case "nextField":
|
|
203
|
+
this.activeField = (this.activeField + 1) % FIELD_ORDER.length;
|
|
204
|
+
return;
|
|
205
|
+
case "space":
|
|
206
|
+
if (this.activeName() === "direction") this.fields.direction = this.fields.direction === "inbox" ? "outbox" : "inbox";
|
|
207
|
+
else this.appendText(data);
|
|
208
|
+
return;
|
|
209
|
+
case "backspace":
|
|
210
|
+
this.backspace();
|
|
211
|
+
return;
|
|
212
|
+
case "submit":
|
|
213
|
+
if (this.activeName() === "body" || this.fields.body.trim()) this.submit();
|
|
214
|
+
else this.activeField = (this.activeField + 1) % FIELD_ORDER.length;
|
|
215
|
+
return;
|
|
179
216
|
}
|
|
217
|
+
// Free-text passthrough (unchanged): any other printable char is typed
|
|
218
|
+
// into the active field.
|
|
180
219
|
if (data.length === 1 && data >= " ") this.appendText(data);
|
|
181
220
|
}
|
|
182
221
|
}
|
|
@@ -1,7 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mailbox detail overlay — RAIL design language (M4/E1, 2026-09-16).
|
|
3
|
+
*
|
|
4
|
+
* ┏ MAILBOX ▸ 1a2b3c4d
|
|
5
|
+
* ┃ Inbox │ Outbox
|
|
6
|
+
* ┃ ›✓ lead ▸ one: ping │ !
|
|
7
|
+
* ┃ ▼ 3 below
|
|
8
|
+
* ┣ MESSAGE
|
|
9
|
+
* ┃ lead ▸ one (one) · pending
|
|
10
|
+
* ┗ Tab side · ↑/↓ select · Enter expand · … ···· Esc close
|
|
11
|
+
*
|
|
12
|
+
* The `│` in the body is a genuine inner COLUMN separator of the two-column
|
|
13
|
+
* mailbox table (§2.D) — it is not a frame edge. The frame is canopy + `┃`
|
|
14
|
+
* body + `┗` close cap; the legacy title/hint header rows, the `───` inline
|
|
15
|
+
* rule before the expanded message and the hand-typed hint string are gone.
|
|
16
|
+
*
|
|
17
|
+
* Hints: built by `formatHint` (close LAST) and laid out with `railLeaders` so
|
|
18
|
+
* the `Esc close` segment stays visible on a narrow overlay instead of being
|
|
19
|
+
* truncated away with the long action list.
|
|
20
|
+
*
|
|
21
|
+
* Every disk-sourced field is guarded (`?? "?"`): mailbox JSON is not
|
|
22
|
+
* schema-validated at read time.
|
|
23
|
+
*/
|
|
24
|
+
|
|
1
25
|
import { type MailboxMessage, readDeliveryState, readMailbox } from "../../state/coordination/mailbox.ts";
|
|
2
26
|
import { loadRunManifestById } from "../../state/stores/state-store.ts";
|
|
3
27
|
import { pad, truncate } from "../../utils/visual.ts";
|
|
4
|
-
import {
|
|
28
|
+
import { overlayActionForKey } from "../keybinding-map.ts";
|
|
29
|
+
import { ACTIVE, CURSOR, canopyLine, formatHint, overflowHint, RAIL, railLeaders, railLine, sectionLine, shortId } from "../rail.ts";
|
|
5
30
|
import { asCrewTheme, type CrewTheme } from "../theme-adapter.ts";
|
|
6
31
|
|
|
7
32
|
export type MailboxAction =
|
|
@@ -11,6 +36,9 @@ export type MailboxAction =
|
|
|
11
36
|
| { type: "ackAll" }
|
|
12
37
|
| { type: "close" };
|
|
13
38
|
|
|
39
|
+
/** Rows shown per column before the overflow hint takes over. */
|
|
40
|
+
const MAX_ROWS = 12;
|
|
41
|
+
|
|
14
42
|
export class MailboxDetailOverlay {
|
|
15
43
|
private readonly runId: string;
|
|
16
44
|
private readonly cwd: string;
|
|
@@ -84,72 +112,124 @@ export class MailboxDetailOverlay {
|
|
|
84
112
|
this.refresh();
|
|
85
113
|
this.needsRefresh = false;
|
|
86
114
|
}
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
115
|
+
if (width < 10) return [];
|
|
116
|
+
const theme = this.theme;
|
|
117
|
+
const budget = width - 2;
|
|
118
|
+
const lines: string[] = [canopyLine({ word: "MAILBOX", subject: shortId(this.runId), theme, budget })];
|
|
119
|
+
|
|
120
|
+
// Two-column body (inbox ↔ outbox): `│` is the inner column separator.
|
|
121
|
+
const col = Math.max(10, Math.floor((budget - 3) / 2));
|
|
122
|
+
const rightCol = Math.max(1, budget - col - 3);
|
|
123
|
+
const twoCol = (left: string, right: string) =>
|
|
124
|
+
`${pad(truncate(left, col), col)} ${theme.fg("dim", "│")} ${truncate(right, rightCol)}`;
|
|
125
|
+
lines.push(railLine(RAIL.body, "border", twoCol(theme.bold("Inbox"), theme.bold("Outbox")), theme, budget));
|
|
126
|
+
const total = Math.max(this.inbox.length, this.outbox.length);
|
|
127
|
+
const shown = Math.min(total, MAX_ROWS);
|
|
128
|
+
for (let index = 0; index < shown; index += 1) {
|
|
129
|
+
lines.push(
|
|
130
|
+
railLine(
|
|
131
|
+
RAIL.body,
|
|
132
|
+
"border",
|
|
133
|
+
twoCol(this.row(this.inbox[index], "inbox", index, col), this.row(this.outbox[index], "outbox", index, col)),
|
|
134
|
+
theme,
|
|
135
|
+
budget,
|
|
136
|
+
),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (total > shown) {
|
|
140
|
+
lines.push(railLine(RAIL.body, "border", overflowHint(0, total - shown, theme), theme, budget));
|
|
97
141
|
}
|
|
142
|
+
|
|
98
143
|
const selected = this.selectedMessage();
|
|
99
144
|
if (this.expanded && selected) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
lines.push(
|
|
145
|
+
const from = selected.from ?? "?";
|
|
146
|
+
const to = selected.to ?? "?";
|
|
147
|
+
lines.push(sectionLine({ name: "Message", theme, budget }));
|
|
148
|
+
lines.push(
|
|
149
|
+
railLine(
|
|
150
|
+
RAIL.body,
|
|
151
|
+
"border",
|
|
152
|
+
truncate(
|
|
153
|
+
`${from} ${ACTIVE} ${to}${selected.taskId ? ` (${selected.taskId})` : ""} · ${selected.status ?? "?"}`,
|
|
154
|
+
budget,
|
|
155
|
+
),
|
|
156
|
+
theme,
|
|
157
|
+
budget,
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
for (const line of (selected.body ?? "").split(/\r?\n/)) {
|
|
161
|
+
lines.push(railLine(RAIL.body, "border", truncate(line, budget), theme, budget));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!this.inbox.length && !this.outbox.length) {
|
|
166
|
+
lines.push(railLine(RAIL.body, "border", theme.fg("dim", "Mailbox is empty."), theme, budget));
|
|
103
167
|
}
|
|
104
|
-
|
|
105
|
-
|
|
168
|
+
|
|
169
|
+
const actions = formatHint([
|
|
170
|
+
["tab", "side"],
|
|
171
|
+
[["up", "down"], "select"],
|
|
172
|
+
["enter", "expand"],
|
|
173
|
+
["A", "ack"],
|
|
174
|
+
["N", "nudge"],
|
|
175
|
+
["C", "compose"],
|
|
176
|
+
["X", "ack all"],
|
|
177
|
+
]);
|
|
178
|
+
const close = formatHint([["escape", "close"]]);
|
|
179
|
+
lines.push(
|
|
180
|
+
railLine(RAIL.close, "border", railLeaders(theme.fg("dim", actions), theme.fg("dim", close), budget, theme), theme, budget),
|
|
181
|
+
);
|
|
182
|
+
return lines;
|
|
106
183
|
}
|
|
107
184
|
|
|
108
185
|
private row(message: MailboxMessage | undefined, side: "inbox" | "outbox", index: number, width: number): string {
|
|
109
|
-
if (!message) return
|
|
110
|
-
const marker = this.side === side && this.selected === index ?
|
|
111
|
-
const
|
|
112
|
-
|
|
186
|
+
if (!message) return "";
|
|
187
|
+
const marker = this.side === side && this.selected === index ? CURSOR : " ";
|
|
188
|
+
const acknowledged = message.status === "acknowledged";
|
|
189
|
+
const status = this.theme.fg(acknowledged ? "success" : "warning", acknowledged ? "✓" : "!");
|
|
190
|
+
const from = message.from ?? "?";
|
|
191
|
+
const to = message.to ?? "?";
|
|
192
|
+
const body = (message.body ?? "").replace(/\s+/g, " ");
|
|
193
|
+
return truncate(`${marker}${status} ${from} ${ACTIVE} ${to}: ${body}`, width);
|
|
113
194
|
}
|
|
114
195
|
|
|
115
196
|
handleInput(data: string): void {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
197
|
+
// M2-1: keys come from the central `overlay:*` keyspace (remappable via
|
|
198
|
+
// `.crew/config.json` → keybindings["overlay:mailbox-detail:<action>"]).
|
|
199
|
+
switch (overlayActionForKey("mailbox-detail", data)) {
|
|
200
|
+
case "close":
|
|
201
|
+
this.done({ type: "close" });
|
|
202
|
+
return;
|
|
203
|
+
case "toggleSide":
|
|
204
|
+
this.side = this.side === "inbox" ? "outbox" : "inbox";
|
|
205
|
+
this.selected = Math.min(this.selected, Math.max(0, this.current().length - 1));
|
|
206
|
+
return;
|
|
207
|
+
case "up":
|
|
208
|
+
this.selected = Math.max(0, this.selected - 1);
|
|
209
|
+
return;
|
|
210
|
+
case "down":
|
|
211
|
+
this.selected = Math.min(Math.max(0, this.current().length - 1), this.selected + 1);
|
|
212
|
+
return;
|
|
213
|
+
case "toggleDetail":
|
|
214
|
+
this.expanded = !this.expanded;
|
|
215
|
+
return;
|
|
216
|
+
case "ack": {
|
|
217
|
+
const message = this.selectedMessage();
|
|
218
|
+
if (message) this.done({ type: "ack", messageId: message.id });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
case "nudge":
|
|
222
|
+
this.done({
|
|
223
|
+
type: "nudge",
|
|
224
|
+
agentId: this.selectedMessage()?.taskId,
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
case "compose":
|
|
228
|
+
this.done({ type: "compose" });
|
|
229
|
+
return;
|
|
230
|
+
case "ackAll":
|
|
231
|
+
this.done({ type: "ackAll" });
|
|
232
|
+
return;
|
|
152
233
|
}
|
|
153
|
-
if (data === "X") this.done({ type: "ackAll" });
|
|
154
234
|
}
|
|
155
235
|
}
|
|
@@ -271,7 +271,6 @@ class PowerbarPublisher {
|
|
|
271
271
|
workspaceId?: string,
|
|
272
272
|
): void {
|
|
273
273
|
if (config?.powerbar === false) return;
|
|
274
|
-
const useStatusFallback = !hasPowerbarConsumer(events);
|
|
275
274
|
// P3 (#10): prefer an explicit workspaceId arg; otherwise self-derive
|
|
276
275
|
// from the ctx every caller already passes (ExtensionContext exposes
|
|
277
276
|
// sessionManager). Back-compat: when neither is present, no filtering.
|