@h-rig/cli 0.0.6-alpha.83 → 0.0.6-alpha.85

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.
Files changed (163) hide show
  1. package/dist/bin/rig.js +22060 -14620
  2. package/dist/src/app/board.js +155 -28
  3. package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
  4. package/dist/src/app-opentui/adapters/doctor.js +1 -25
  5. package/dist/src/app-opentui/adapters/family.d.ts +62 -0
  6. package/dist/src/app-opentui/adapters/family.js +14305 -0
  7. package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
  8. package/dist/src/app-opentui/adapters/fleet.js +6 -40
  9. package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
  10. package/dist/src/app-opentui/adapters/inbox.js +54 -59
  11. package/dist/src/app-opentui/adapters/init.d.ts +0 -2
  12. package/dist/src/app-opentui/adapters/init.js +0 -26
  13. package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
  14. package/dist/src/app-opentui/adapters/inspect.js +1024 -0
  15. package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
  16. package/dist/src/app-opentui/adapters/pi-attach.js +348 -93
  17. package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
  18. package/dist/src/app-opentui/adapters/pi.js +363 -0
  19. package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
  20. package/dist/src/app-opentui/adapters/plugin.js +544 -0
  21. package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
  22. package/dist/src/app-opentui/adapters/repo.js +186 -0
  23. package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
  24. package/dist/src/app-opentui/adapters/run-detail.js +97 -44
  25. package/dist/src/app-opentui/adapters/server.d.ts +10 -2
  26. package/dist/src/app-opentui/adapters/server.js +96 -27
  27. package/dist/src/app-opentui/adapters/tasks.d.ts +0 -2
  28. package/dist/src/app-opentui/adapters/tasks.js +437 -105
  29. package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
  30. package/dist/src/app-opentui/adapters/workspace.js +333 -0
  31. package/dist/src/app-opentui/autocomplete.d.ts +20 -0
  32. package/dist/src/app-opentui/autocomplete.js +576 -0
  33. package/dist/src/app-opentui/bootstrap.js +24313 -16963
  34. package/dist/src/app-opentui/command-palette.d.ts +3 -0
  35. package/dist/src/app-opentui/command-palette.js +1010 -0
  36. package/dist/src/app-opentui/drone.js +8 -6
  37. package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
  38. package/dist/src/app-opentui/fleet-stats.js +114 -0
  39. package/dist/src/app-opentui/index.js +3438 -1724
  40. package/dist/src/app-opentui/intent.js +71 -48
  41. package/dist/src/app-opentui/keymap.d.ts +6 -5
  42. package/dist/src/app-opentui/keymap.js +1064 -23
  43. package/dist/src/app-opentui/layout.d.ts +7 -0
  44. package/dist/src/app-opentui/layout.js +13 -6
  45. package/dist/src/app-opentui/list-search.d.ts +24 -0
  46. package/dist/src/app-opentui/list-search.js +88 -1
  47. package/dist/src/app-opentui/pi-host-child.js +33 -1
  48. package/dist/src/app-opentui/pi-pty-host.d.ts +5 -0
  49. package/dist/src/app-opentui/pi-pty-host.js +21 -3
  50. package/dist/src/app-opentui/react/App.d.ts +9 -0
  51. package/dist/src/app-opentui/react/App.js +5099 -0
  52. package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
  53. package/dist/src/app-opentui/react/Backdrop.js +829 -0
  54. package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
  55. package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
  56. package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
  57. package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
  58. package/dist/src/app-opentui/react/context.d.ts +17 -0
  59. package/dist/src/app-opentui/react/context.js +37 -0
  60. package/dist/src/app-opentui/react/launch.d.ts +2 -0
  61. package/dist/src/app-opentui/react/launch.js +5698 -0
  62. package/dist/src/app-opentui/react/nav.d.ts +18 -0
  63. package/dist/src/app-opentui/react/nav.js +54 -0
  64. package/dist/src/app-opentui/react/scroll.d.ts +12 -0
  65. package/dist/src/app-opentui/react/scroll.js +21 -0
  66. package/dist/src/app-opentui/react/syntax.d.ts +2 -0
  67. package/dist/src/app-opentui/react/syntax.js +64 -0
  68. package/dist/src/app-opentui/registry.js +19923 -5151
  69. package/dist/src/app-opentui/render/constants.d.ts +31 -0
  70. package/dist/src/app-opentui/render/constants.js +66 -0
  71. package/dist/src/app-opentui/render/graphics.d.ts +2 -1
  72. package/dist/src/app-opentui/render/graphics.js +106 -39
  73. package/dist/src/app-opentui/render/image-visual-layer-worker.js +108 -20
  74. package/dist/src/app-opentui/render/image-visual-layer.d.ts +7 -0
  75. package/dist/src/app-opentui/render/image-visual-layer.js +109 -20
  76. package/dist/src/app-opentui/render/native-host.d.ts +37 -0
  77. package/dist/src/app-opentui/render/native-host.js +179 -0
  78. package/dist/src/app-opentui/render/panels.js +18 -11
  79. package/dist/src/app-opentui/render/preloader.js +8 -6
  80. package/dist/src/app-opentui/render/scene.d.ts +50 -1
  81. package/dist/src/app-opentui/render/scene.js +183 -6
  82. package/dist/src/app-opentui/render/text.d.ts +7 -1
  83. package/dist/src/app-opentui/render/text.js +10 -7
  84. package/dist/src/app-opentui/render/type-bar.js +69 -30
  85. package/dist/src/app-opentui/runtime.d.ts +44 -1
  86. package/dist/src/app-opentui/runtime.js +3231 -1363
  87. package/dist/src/app-opentui/scenes/command.js +20 -6
  88. package/dist/src/app-opentui/scenes/doctor.js +176 -11
  89. package/dist/src/app-opentui/scenes/error.js +20 -10
  90. package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
  91. package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
  92. package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
  93. package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
  94. package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
  95. package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
  96. package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
  97. package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
  98. package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
  99. package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
  100. package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
  101. package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
  102. package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
  103. package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
  104. package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
  105. package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
  106. package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
  107. package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
  108. package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
  109. package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
  110. package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
  111. package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
  112. package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
  113. package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
  114. package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
  115. package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
  116. package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
  117. package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
  118. package/dist/src/app-opentui/scenes/family.d.ts +3 -0
  119. package/dist/src/app-opentui/scenes/family.js +2144 -0
  120. package/dist/src/app-opentui/scenes/fleet.js +521 -31
  121. package/dist/src/app-opentui/scenes/handoff.js +204 -12
  122. package/dist/src/app-opentui/scenes/help.js +609 -40
  123. package/dist/src/app-opentui/scenes/inbox.js +278 -17
  124. package/dist/src/app-opentui/scenes/init.js +84 -39
  125. package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
  126. package/dist/src/app-opentui/scenes/inspect.js +793 -0
  127. package/dist/src/app-opentui/scenes/main.js +218 -39
  128. package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
  129. package/dist/src/app-opentui/scenes/pi.js +508 -0
  130. package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
  131. package/dist/src/app-opentui/scenes/plugin.js +486 -0
  132. package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
  133. package/dist/src/app-opentui/scenes/repo.js +424 -0
  134. package/dist/src/app-opentui/scenes/run-detail.js +333 -20
  135. package/dist/src/app-opentui/scenes/server.js +181 -15
  136. package/dist/src/app-opentui/scenes/tasks.js +486 -31
  137. package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
  138. package/dist/src/app-opentui/scenes/workspace.js +426 -0
  139. package/dist/src/app-opentui/selectable.d.ts +19 -0
  140. package/dist/src/app-opentui/selectable.js +79 -0
  141. package/dist/src/app-opentui/state.js +83 -30
  142. package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
  143. package/dist/src/app-opentui/surface-catalog.js +540 -0
  144. package/dist/src/app-opentui/theme.d.ts +28 -6
  145. package/dist/src/app-opentui/theme.js +61 -8
  146. package/dist/src/app-opentui/types.d.ts +121 -4
  147. package/dist/src/commands/_authority-runs.d.ts +1 -1
  148. package/dist/src/commands/_authority-runs.js +2 -12
  149. package/dist/src/commands/_help-catalog.js +95 -15
  150. package/dist/src/commands/_operator-view.js +34 -2
  151. package/dist/src/commands/_pi-frontend.d.ts +2 -0
  152. package/dist/src/commands/_pi-frontend.js +34 -0
  153. package/dist/src/commands/_server-events.d.ts +26 -0
  154. package/dist/src/commands/_server-events.js +310 -0
  155. package/dist/src/commands/agent.js +2 -12
  156. package/dist/src/commands/run.js +34 -2
  157. package/dist/src/commands/server.js +2 -12
  158. package/dist/src/commands/stats.js +95 -15
  159. package/dist/src/commands/task-run-driver.js +2 -12
  160. package/dist/src/commands/task.js +131 -29
  161. package/dist/src/commands.js +192 -31
  162. package/dist/src/index.js +192 -31
  163. package/package.json +11 -9
@@ -11,15 +11,17 @@ var RIG_UI = {
11
11
  bg: "#070809",
12
12
  bg2: "#0b0c0e",
13
13
  panel: "#101115",
14
- panel2: "#101115",
15
- glass: "#14161b",
16
- ink: "#f2f3f6",
17
- ink2: "#aeb0ba",
18
- ink3: "#6c6e79",
19
- ink4: "#44464f",
14
+ panel2: "#14161b",
15
+ glass: "#1e2230",
16
+ border: "#2a2e3a",
17
+ ink: "#f4f5f8",
18
+ ink2: "#c7c9d3",
19
+ ink3: "#9aa0ae",
20
+ ink4: "#787b86",
20
21
  lime: "#ccff4d",
21
22
  limeDim: "#a9d63f",
22
23
  cyan: "#56d8ff",
24
+ cyanDim: "#3f9fbd",
23
25
  red: "#ff5d5d",
24
26
  yellow: "#ffd24d",
25
27
  magenta: "#ff79b0"
@@ -37,6 +39,18 @@ var styles = {
37
39
  magenta: otuiFg(RIG_UI.magenta)
38
40
  };
39
41
 
42
+ // packages/cli/src/app-opentui/drone.ts
43
+ import { RGBA, StyledText, TextAttributes as TextAttributes2 } from "@opentui/core";
44
+ var COLOR = {
45
+ body: RGBA.fromHex(RIG_UI.lime),
46
+ mini: RGBA.fromHex(RIG_UI.limeDim),
47
+ rotor: RGBA.fromHex(RIG_UI.cyan),
48
+ path: RGBA.fromHex(RIG_UI.cyan),
49
+ eye: RGBA.fromHex(RIG_UI.ink),
50
+ dim: RGBA.fromHex(RIG_UI.ink4),
51
+ ink: RGBA.fromHex(RIG_UI.ink2)
52
+ };
53
+
40
54
  // packages/cli/src/app-opentui/render/scene.ts
41
55
  function line(text, options = {}) {
42
56
  return { text, ...options };
@@ -55,6 +69,11 @@ function makeSceneFrame(input) {
55
69
  return input;
56
70
  }
57
71
 
72
+ // packages/cli/src/app-opentui/selectable.ts
73
+ function selectableDeckRow(deck, item) {
74
+ return { ...deckRow({ ...deck, activateOnClick: true }), selectable: item };
75
+ }
76
+
58
77
  // packages/cli/src/app-opentui/render/ascii-fleet.ts
59
78
  var LEAD_DRONE = [
60
79
  " .-=-. .-=-. ",
@@ -133,6 +152,35 @@ function fleetRows(tick, options = {}) {
133
152
  }
134
153
 
135
154
  // packages/cli/src/app-opentui/scenes/handoff.ts
155
+ function elapsedLabel(step, nowMs) {
156
+ const end = step.endedAtMs ?? nowMs;
157
+ const seconds = Math.max(0, (end - step.startedAtMs) / 1000);
158
+ return `${seconds.toFixed(1)}s`;
159
+ }
160
+ function prepStepLines(handoff) {
161
+ const now = Date.now();
162
+ const steps = handoff.steps ?? [];
163
+ const out = [
164
+ line(`Preparing bundled Pi for ${handoff.runId.slice(0, 8)}`, { fg: RIG_UI.ink, bold: true }),
165
+ line("connect \u2192 fetch transcript \u2192 spawn host \u2192 start Pi", { fg: RIG_UI.ink4 }),
166
+ line("", {}),
167
+ line("STEPS", { fg: RIG_UI.ink3, bold: true })
168
+ ];
169
+ for (const step of steps) {
170
+ const glyph = step.status === "done" ? "\u2713" : step.status === "failed" ? "\u2717" : "\u283F";
171
+ const color = step.status === "failed" ? RIG_UI.red : step.status === "done" ? RIG_UI.limeDim : RIG_UI.cyan;
172
+ out.push(line(`${glyph} ${step.label} \xB7 ${elapsedLabel(step, now)}`, { fg: color }));
173
+ }
174
+ if (steps.length === 0)
175
+ out.push(line("\u283F connecting\u2026", { fg: RIG_UI.cyan }));
176
+ return out;
177
+ }
178
+ function handoffAttachItem(runId) {
179
+ return { id: "attach", label: "attach", intent: { scene: "handoff", argv: ["attach", runId], action: { kind: "run-attach", payload: { runId }, label: `Attach Pi ${runId.slice(0, 8)}` } }, message: "open bundled Pi" };
180
+ }
181
+ var HANDOFF_RUNS_ITEM = { id: "runs", label: "runs", intent: { scene: "fleet", argv: ["fleet"], action: { kind: "refresh", label: "Back to fleet" } }, message: "back to fleet" };
182
+ var HANDOFF_RUNS_PICK_ITEM = { id: "runs", label: "runs", intent: { scene: "fleet", argv: ["fleet"], action: { kind: "refresh", label: "Open runs" } }, message: "select a run first" };
183
+ var HANDOFF_TASKS_ITEM = { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Open tasks" } }, message: "open tasks" };
136
184
  function handoffState(state) {
137
185
  const value = state.data.piAttach ?? state.data.handoff;
138
186
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -147,8 +195,9 @@ function clip(value, width) {
147
195
  return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
148
196
  }
149
197
  function terminalRail(snapshot) {
150
- const text = ` run ${snapshot.runId.slice(0, 8)} \xB7 bundled Pi \xB7 mouse wheel scroll \xB7 ctrl-] detach \xB7 ${snapshot.message ?? snapshot.status}`;
151
- return line(clip(text, snapshot.cols), { fg: RIG_UI.limeDim });
198
+ const controls = snapshot.status === "failed" ? "failed \xB7 r runs \xB7 enter retry" : "bundled Pi \xB7 mouse wheel scroll \xB7 ctrl-] detach";
199
+ const text = ` run ${snapshot.runId.slice(0, 8)} \xB7 ${controls} \xB7 ${snapshot.message ?? snapshot.status}`;
200
+ return line(clip(text, snapshot.cols), { fg: snapshot.status === "failed" ? RIG_UI.red : RIG_UI.limeDim });
152
201
  }
153
202
  function shouldShowTerminalPreloader(snapshot) {
154
203
  if (snapshot.status !== "starting" && snapshot.status !== "running")
@@ -178,8 +227,125 @@ function terminalLines(snapshot, tick) {
178
227
  const body = shouldShowTerminalPreloader(snapshot) ? terminalPreloaderLines(snapshot, tick) : !hasVisibleOutput && snapshot.status === "running" ? [line(`starting bundled Pi for ${snapshot.runId.slice(0, 8)}\u2026`, { fg: RIG_UI.ink3 })] : snapshot.lines.map((text, index) => line(text, { fg: RIG_UI.ink, styledText: snapshot.styledLines?.[index] }));
179
228
  return [...body, terminalRail(snapshot)];
180
229
  }
230
+ function entryText(entry) {
231
+ return String(entry.detail ?? entry.message ?? entry.text ?? entry.title ?? entry.type ?? "event").replace(/\s+/g, " ").slice(0, 200);
232
+ }
233
+ function inspectSnapshot(state) {
234
+ const inspect = state.data.inspect;
235
+ if (!inspect || typeof inspect !== "object" || Array.isArray(inspect))
236
+ return null;
237
+ return inspect;
238
+ }
239
+ function rerunItem(runId, taskId) {
240
+ if (taskId) {
241
+ return {
242
+ id: "rerun",
243
+ label: "rerun",
244
+ intent: { scene: "fleet", argv: ["task", "run", taskId], action: { kind: "task-run-id", payload: { task: taskId }, label: `Rerun task ${taskId}` } },
245
+ message: "re-dispatch this task as a new run"
246
+ };
247
+ }
248
+ return {
249
+ id: "rerun",
250
+ label: "rerun",
251
+ intent: { scene: "command", argv: ["run", "retry", runId], action: { kind: "command-run", payload: { runId }, label: `Rerun ${runId.slice(0, 8)}` } },
252
+ message: "re-run this run"
253
+ };
254
+ }
255
+ function inspectItem(runId) {
256
+ return {
257
+ id: "inspect",
258
+ label: "inspect",
259
+ data: { selectedRunId: runId },
260
+ intent: { scene: "inspect", argv: ["inspect", runId], action: { kind: "refresh", payload: { runId }, label: `Inspect ${runId.slice(0, 8)}` } },
261
+ message: "logs / timeline / diff / failures"
262
+ };
263
+ }
264
+ function failureReason(state, terminal) {
265
+ const handoff = handoffState(state);
266
+ if (handoff?.failureReason?.trim())
267
+ return handoff.failureReason.trim();
268
+ if (terminal.message?.trim())
269
+ return terminal.message.trim();
270
+ const snap = inspectSnapshot(state);
271
+ const firstFailure = Array.isArray(snap?.failures) && snap.failures.length > 0 ? snap.failures[snap.failures.length - 1] : null;
272
+ if (firstFailure)
273
+ return entryText(firstFailure);
274
+ const lastError = typeof state.data.lastRefreshError === "string" ? state.data.lastRefreshError.trim() : "";
275
+ if (lastError)
276
+ return lastError;
277
+ return `bundled Pi exited with code ${terminal.exitCode ?? "unknown"}`;
278
+ }
279
+ function failedSurfaceLines(state, terminal) {
280
+ const runId = terminal.runId;
281
+ const snap = inspectSnapshot(state);
282
+ const taskId = snap?.taskId;
283
+ const selected = Math.max(0, state.selection.index);
284
+ const out = [
285
+ line("", {}),
286
+ line(" \u2717 RUN FAILED", { fg: RIG_UI.red, bold: true }),
287
+ line(` run ${runId.slice(0, 8)}${taskId ? ` \xB7 task ${taskId}` : ""}`, { fg: RIG_UI.ink3 }),
288
+ line("", {}),
289
+ line(" REASON", { fg: RIG_UI.ink3, bold: true }),
290
+ line(` ${failureReason(state, terminal)}`, { fg: RIG_UI.red }),
291
+ line("", {}),
292
+ line(" ACTIONS", { fg: RIG_UI.ink3, bold: true }),
293
+ selectableDeckRow({ label: "rerun", detail: taskId ? "re-dispatch this task as a new run" : "re-run this run", index: 0, active: selected === 0 }, rerunItem(runId, taskId)),
294
+ selectableDeckRow({ label: "inspect", detail: "logs / timeline / diff / failures", index: 1, active: selected === 1 }, inspectItem(runId)),
295
+ selectableDeckRow({ label: "runs", detail: "return to fleet", index: 2, active: selected === 2 }, HANDOFF_RUNS_ITEM)
296
+ ];
297
+ const section = (title, rows, color, limit) => {
298
+ const records = Array.isArray(rows) ? rows.slice(-limit) : [];
299
+ if (records.length === 0)
300
+ return;
301
+ out.push(line("", {}), line(` ${title} (${Array.isArray(rows) ? rows.length : 0})`, { fg: RIG_UI.ink3, bold: true }));
302
+ for (const entry of records)
303
+ out.push(line(` \xB7 ${entryText(entry)}`, { fg: color }));
304
+ };
305
+ section("FAILURES", snap?.failures, RIG_UI.red, 12);
306
+ section("TIMELINE", snap?.timeline, RIG_UI.ink2, 20);
307
+ section("LOGS", snap?.logs, RIG_UI.ink3, 30);
308
+ if (snap && typeof snap.diff === "string" && snap.diff.trim()) {
309
+ const diffLines = snap.diff.split(`
310
+ `).slice(0, 80);
311
+ out.push(line("", {}), line(" DIFF", { fg: RIG_UI.ink3, bold: true }));
312
+ for (const text of diffLines) {
313
+ out.push(line(` ${text}`, { fg: text.startsWith("+") ? RIG_UI.limeDim : text.startsWith("-") ? RIG_UI.red : RIG_UI.ink3 }));
314
+ }
315
+ }
316
+ return out;
317
+ }
181
318
  function renderHandoffScene(state) {
182
319
  const terminal = terminalState(state);
320
+ if (terminal && terminal.status === "failed") {
321
+ return makeSceneFrame({
322
+ scene: "handoff",
323
+ title: "Pi",
324
+ lines: [],
325
+ panels: [{
326
+ id: `pi-failed-${terminal.runId}`,
327
+ top: 0,
328
+ left: 0,
329
+ width: terminal.cols,
330
+ height: terminal.rows,
331
+ lines: failedSurfaceLines(state, terminal),
332
+ backgroundColor: "#0a0506",
333
+ backgroundAlpha: 200,
334
+ opacity: 1,
335
+ border: false,
336
+ chrome: "ad-terminal",
337
+ headerText: "run failed \xB7 investigate / rerun",
338
+ headerHeight: 3,
339
+ paddingX: 2,
340
+ paddingY: 1,
341
+ stickyScroll: true,
342
+ stickyStart: "top"
343
+ }],
344
+ footer: { run: terminal.runId.slice(0, 8), message: "\u2717 failed \xB7 enter rerun \xB7 inspect for detail" },
345
+ live: true,
346
+ fullScreen: true
347
+ });
348
+ }
183
349
  if (terminal && (terminal.status === "starting" || terminal.status === "running")) {
184
350
  return makeSceneFrame({
185
351
  scene: "handoff",
@@ -207,6 +373,32 @@ function renderHandoffScene(state) {
207
373
  });
208
374
  }
209
375
  const handoff = handoffState(state);
376
+ if (!terminal && handoff && handoff.status === "entering-pi" && (handoff.steps?.length ?? 0) > 0) {
377
+ return makeSceneFrame({
378
+ scene: "handoff",
379
+ title: "Pi handoff",
380
+ lines: [],
381
+ panels: [{
382
+ id: `pi-prep-${handoff.runId}`,
383
+ top: 0,
384
+ width: 96,
385
+ height: 18,
386
+ lines: prepStepLines(handoff),
387
+ backgroundColor: RIG_UI.panel,
388
+ backgroundAlpha: 184,
389
+ opacity: 1,
390
+ border: false,
391
+ chrome: "ad-terminal",
392
+ headerText: "preparing bundled Pi",
393
+ headerHeight: 3,
394
+ paddingX: 3,
395
+ paddingY: 1
396
+ }],
397
+ footer: { run: `${handoff.runId.slice(0, 8)} preparing`, message: handoff.message ?? "preparing bundled Pi" },
398
+ hideTypeBar: true,
399
+ live: true
400
+ });
401
+ }
210
402
  const runId = handoff?.runId ?? terminal?.runId ?? (typeof state.data.selectedRunId === "string" ? state.data.selectedRunId : undefined);
211
403
  const status = handoff?.status ?? (runId ? "preparing" : "select-run");
212
404
  const message = handoff?.message ?? (runId ? "ready to start bundled Pi inside this app" : "choose a run before entering Pi");
@@ -232,14 +424,14 @@ function renderHandoffScene(state) {
232
424
  line(`message ${message}`, { fg: RIG_UI.ink2 }),
233
425
  line("", { fg: RIG_UI.ink3 }),
234
426
  line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
235
- deckRow({ label: "attach", detail: "open bundled Pi", index: 0, active: selected === 0, activateOnClick: true }),
236
- deckRow({ label: "runs", detail: "return to fleet", index: 1, active: selected === 1, activateOnClick: true })
427
+ selectableDeckRow({ label: "attach", detail: "open bundled Pi", index: 0, active: selected === 0 }, handoffAttachItem(runId)),
428
+ selectableDeckRow({ label: "runs", detail: "return to fleet", index: 1, active: selected === 1 }, HANDOFF_RUNS_ITEM)
237
429
  ] : [
238
430
  line(`message ${message}`, { fg: RIG_UI.ink2 }),
239
431
  line("", { fg: RIG_UI.ink3 }),
240
432
  line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
241
- deckRow({ label: "runs", detail: "select a run first", index: 0, active: selected === 0, activateOnClick: true }),
242
- deckRow({ label: "tasks", detail: "open tasks to dispatch work", index: 1, active: selected === 1, activateOnClick: true })
433
+ selectableDeckRow({ label: "runs", detail: "select a run first", index: 0, active: selected === 0 }, HANDOFF_RUNS_PICK_ITEM),
434
+ selectableDeckRow({ label: "tasks", detail: "open tasks to dispatch work", index: 1, active: selected === 1 }, HANDOFF_TASKS_ITEM)
243
435
  ]
244
436
  ],
245
437
  backgroundColor: RIG_UI.panel,