@pellux/goodvibes-tui 1.1.0 → 1.9.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.
Files changed (267) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +28 -18
  3. package/docs/foundation-artifacts/operator-contract.json +4045 -1763
  4. package/package.json +2 -2
  5. package/src/audio/spoken-turn-controller.ts +12 -2
  6. package/src/audio/spoken-turn-wiring.ts +2 -1
  7. package/src/cli/help.ts +8 -1
  8. package/src/cli/service-posture.ts +2 -1
  9. package/src/cli/status.ts +2 -1
  10. package/src/cli/surface-command.ts +2 -2
  11. package/src/core/alert-gating.ts +67 -0
  12. package/src/core/approval-alert.ts +72 -0
  13. package/src/core/budget-breach-notifier.ts +106 -0
  14. package/src/core/composer-state.ts +11 -3
  15. package/src/core/conversation-line-cache.ts +27 -3
  16. package/src/core/conversation-rendering.ts +90 -14
  17. package/src/core/conversation.ts +18 -0
  18. package/src/core/focus-tracker.ts +41 -0
  19. package/src/core/long-task-notifier.ts +33 -6
  20. package/src/core/stream-event-wiring.ts +20 -1
  21. package/src/core/system-message-noise.ts +87 -0
  22. package/src/core/system-message-router.ts +104 -51
  23. package/src/core/turn-cancellation.ts +25 -0
  24. package/src/core/turn-event-wiring.ts +72 -3
  25. package/src/daemon/cli.ts +29 -2
  26. package/src/daemon/handlers/register.ts +8 -1
  27. package/src/daemon/service-commands.ts +329 -0
  28. package/src/export/cost-utils.ts +36 -0
  29. package/src/input/autocomplete.ts +27 -1
  30. package/src/input/command-registry.ts +84 -5
  31. package/src/input/commands/checkpoint-runtime.ts +280 -0
  32. package/src/input/commands/codebase-runtime.ts +232 -0
  33. package/src/input/commands/config.ts +43 -3
  34. package/src/input/commands/eval.ts +1 -1
  35. package/src/input/commands/health-runtime.ts +10 -2
  36. package/src/input/commands/image-runtime.ts +112 -0
  37. package/src/input/commands/intelligence-runtime.ts +11 -1
  38. package/src/input/commands/local-auth-runtime.ts +4 -1
  39. package/src/input/commands/local-runtime.ts +9 -3
  40. package/src/input/commands/marketplace-runtime.ts +2 -2
  41. package/src/input/commands/memory.ts +73 -35
  42. package/src/input/commands/operator-panel-runtime.ts +71 -31
  43. package/src/input/commands/planning-runtime.ts +116 -4
  44. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  45. package/src/input/commands/plugin-runtime.ts +2 -2
  46. package/src/input/commands/policy-dispatch.ts +21 -0
  47. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  48. package/src/input/commands/qrcode-runtime.ts +26 -6
  49. package/src/input/commands/recall-review.ts +35 -0
  50. package/src/input/commands/remote-runtime-setup.ts +5 -3
  51. package/src/input/commands/remote-runtime.ts +3 -3
  52. package/src/input/commands/runtime-services.ts +54 -13
  53. package/src/input/commands/services-runtime.ts +1 -1
  54. package/src/input/commands/session-content.ts +20 -9
  55. package/src/input/commands/session-workflow.ts +16 -1
  56. package/src/input/commands/settings-sync-runtime.ts +16 -4
  57. package/src/input/commands/shell-core.ts +25 -8
  58. package/src/input/commands/skills-runtime.ts +2 -8
  59. package/src/input/commands/subscription-runtime.ts +2 -2
  60. package/src/input/commands/test-runtime.ts +277 -0
  61. package/src/input/commands/websearch-runtime.ts +101 -0
  62. package/src/input/commands/work-plan-runtime.ts +36 -10
  63. package/src/input/commands/workstream-runtime.ts +488 -0
  64. package/src/input/commands.ts +12 -0
  65. package/src/input/config-modal-types.ts +175 -0
  66. package/src/input/config-modal.ts +592 -0
  67. package/src/input/feed-context-factory.ts +10 -8
  68. package/src/input/handler-command-route.ts +37 -20
  69. package/src/input/handler-content-actions.ts +17 -2
  70. package/src/input/handler-feed-routes.ts +74 -114
  71. package/src/input/handler-feed.ts +79 -27
  72. package/src/input/handler-interactions.ts +1 -3
  73. package/src/input/handler-modal-routes.ts +136 -6
  74. package/src/input/handler-modal-stack.ts +14 -7
  75. package/src/input/handler-modal-token-routes.ts +16 -49
  76. package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
  77. package/src/input/handler-onboarding.ts +71 -59
  78. package/src/input/handler-picker-routes.ts +26 -102
  79. package/src/input/handler-shortcuts.ts +74 -14
  80. package/src/input/handler-types.ts +2 -6
  81. package/src/input/handler-ui-state.ts +10 -22
  82. package/src/input/handler.ts +12 -29
  83. package/src/input/keybindings.ts +23 -8
  84. package/src/input/model-picker-types.ts +24 -6
  85. package/src/input/model-picker.ts +77 -2
  86. package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
  87. package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
  88. package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
  89. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
  90. package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
  91. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  92. package/src/input/panel-integration-actions.ts +9 -170
  93. package/src/input/panel-mouse-geometry.ts +97 -0
  94. package/src/input/panel-paste-flood-guard.ts +86 -0
  95. package/src/input/selection-modal.ts +11 -0
  96. package/src/input/session-picker-modal.ts +44 -4
  97. package/src/input/settings-modal-data.ts +146 -2
  98. package/src/input/settings-modal-mutations.ts +6 -4
  99. package/src/input/settings-modal-types.ts +4 -2
  100. package/src/main.ts +49 -51
  101. package/src/panels/agent-inspector-shared.ts +2 -1
  102. package/src/panels/base-panel.ts +22 -1
  103. package/src/panels/builtin/agent.ts +21 -107
  104. package/src/panels/builtin/development.ts +15 -61
  105. package/src/panels/builtin/knowledge.ts +8 -32
  106. package/src/panels/builtin/operations.ts +84 -428
  107. package/src/panels/builtin/session.ts +21 -112
  108. package/src/panels/builtin/shared.ts +18 -46
  109. package/src/panels/builtin-modals.ts +218 -0
  110. package/src/panels/builtin-panels.ts +5 -0
  111. package/src/panels/cost-tracker-panel.ts +36 -10
  112. package/src/panels/diff-panel.ts +12 -43
  113. package/src/panels/eval-registry.ts +60 -0
  114. package/src/panels/fleet-deep-link.ts +31 -0
  115. package/src/panels/fleet-panel-format.ts +62 -0
  116. package/src/panels/fleet-panel-worktree-detail.ts +48 -0
  117. package/src/panels/fleet-panel.ts +758 -0
  118. package/src/panels/fleet-read-model.ts +520 -0
  119. package/src/panels/fleet-steer.ts +125 -0
  120. package/src/panels/fleet-stop.ts +153 -0
  121. package/src/panels/fleet-tabs.ts +230 -0
  122. package/src/panels/fleet-transcript.ts +356 -0
  123. package/src/panels/index.ts +7 -31
  124. package/src/panels/modals/hooks-modal.ts +187 -0
  125. package/src/panels/modals/keybindings-modal.ts +166 -0
  126. package/src/panels/modals/knowledge-modal.ts +158 -0
  127. package/src/panels/modals/local-auth-modal.ts +132 -0
  128. package/src/panels/modals/marketplace-modal.ts +218 -0
  129. package/src/panels/modals/memory-modal.ts +180 -0
  130. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  131. package/src/panels/modals/modal-theme.ts +42 -0
  132. package/src/panels/modals/pairing-modal.ts +163 -0
  133. package/src/panels/modals/planning-modal.ts +304 -0
  134. package/src/panels/modals/plugins-modal.ts +174 -0
  135. package/src/panels/modals/policy-modal.ts +256 -0
  136. package/src/panels/modals/provider-health-modal.ts +136 -0
  137. package/src/panels/modals/remote-modal.ts +115 -0
  138. package/src/panels/modals/sandbox-modal.ts +150 -0
  139. package/src/panels/modals/security-modal.ts +217 -0
  140. package/src/panels/modals/services-modal.ts +179 -0
  141. package/src/panels/modals/settings-sync-modal.ts +142 -0
  142. package/src/panels/modals/skills-modal.ts +189 -0
  143. package/src/panels/modals/subscription-modal.ts +172 -0
  144. package/src/panels/modals/work-plan-modal.ts +177 -0
  145. package/src/panels/panel-confirm-overlay.ts +72 -0
  146. package/src/panels/panel-manager.ts +123 -9
  147. package/src/panels/polish-core.ts +38 -25
  148. package/src/panels/project-planning-answer-actions.ts +8 -11
  149. package/src/panels/skills-panel.ts +7 -51
  150. package/src/panels/types.ts +37 -0
  151. package/src/permissions/hunk-selection.ts +179 -0
  152. package/src/permissions/prompt.ts +216 -13
  153. package/src/renderer/autocomplete-overlay.ts +28 -2
  154. package/src/renderer/compaction-history-modal.ts +19 -3
  155. package/src/renderer/compaction-preview.ts +13 -2
  156. package/src/renderer/compaction-quality.ts +100 -0
  157. package/src/renderer/compositor.ts +2 -3
  158. package/src/renderer/config-modal.ts +95 -0
  159. package/src/renderer/conversation-overlays.ts +10 -17
  160. package/src/renderer/fleet-tab-strip.ts +56 -0
  161. package/src/renderer/footer-tips.ts +15 -3
  162. package/src/renderer/fullscreen-primitives.ts +32 -22
  163. package/src/renderer/git-status.ts +43 -1
  164. package/src/renderer/help-overlay.ts +2 -2
  165. package/src/renderer/layout.ts +0 -4
  166. package/src/renderer/markdown.ts +7 -3
  167. package/src/renderer/modal-factory.ts +25 -20
  168. package/src/renderer/model-workspace.ts +62 -4
  169. package/src/renderer/overlay-box.ts +21 -17
  170. package/src/renderer/panel-workspace-bar.ts +8 -2
  171. package/src/renderer/process-indicator.ts +14 -3
  172. package/src/renderer/selection-modal-overlay.ts +6 -1
  173. package/src/renderer/session-picker-modal.ts +196 -3
  174. package/src/renderer/settings-modal-helpers.ts +2 -0
  175. package/src/renderer/settings-modal.ts +7 -0
  176. package/src/renderer/shell-surface.ts +8 -1
  177. package/src/renderer/status-glyphs.ts +14 -15
  178. package/src/renderer/system-message.ts +15 -3
  179. package/src/renderer/terminal-bg-probe.ts +339 -0
  180. package/src/renderer/terminal-escapes.ts +20 -0
  181. package/src/renderer/theme-mode-config.ts +67 -0
  182. package/src/renderer/theme.ts +91 -1
  183. package/src/renderer/thinking.ts +11 -3
  184. package/src/renderer/tool-call.ts +15 -9
  185. package/src/renderer/tool-result-summary.ts +148 -0
  186. package/src/renderer/turn-injection.ts +133 -0
  187. package/src/renderer/ui-factory.ts +154 -85
  188. package/src/renderer/ui-primitives.ts +30 -129
  189. package/src/runtime/bootstrap-command-context.ts +27 -1
  190. package/src/runtime/bootstrap-command-parts.ts +41 -10
  191. package/src/runtime/bootstrap-core.ts +47 -15
  192. package/src/runtime/bootstrap-hook-bridge.ts +7 -0
  193. package/src/runtime/bootstrap-shell.ts +48 -17
  194. package/src/runtime/bootstrap.ts +129 -22
  195. package/src/runtime/code-index-services.ts +135 -0
  196. package/src/runtime/legacy-daemon-migration.ts +516 -0
  197. package/src/runtime/memory-fold.ts +26 -0
  198. package/src/runtime/onboarding/derivation.ts +7 -2
  199. package/src/runtime/onboarding/snapshot.ts +27 -1
  200. package/src/runtime/onboarding/types.ts +31 -1
  201. package/src/runtime/operator-token-cleanup.ts +82 -1
  202. package/src/runtime/orchestrator-core-services.ts +59 -0
  203. package/src/runtime/process-lifecycle.ts +3 -1
  204. package/src/runtime/resume-notice.ts +209 -0
  205. package/src/runtime/services.ts +98 -46
  206. package/src/runtime/session-inbound-inputs.ts +252 -0
  207. package/src/runtime/session-spine-transport.ts +64 -0
  208. package/src/runtime/terminal-output-guard.ts +15 -8
  209. package/src/runtime/ui-services.ts +27 -3
  210. package/src/runtime/workstream-services.ts +327 -0
  211. package/src/runtime/wrfc-persistence.ts +124 -17
  212. package/src/shell/blocking-input.ts +68 -4
  213. package/src/shell/recovery-input-helpers.ts +170 -1
  214. package/src/shell/ui-openers.ts +171 -26
  215. package/src/utils/format-duration.ts +8 -18
  216. package/src/utils/splash-lines.ts +1 -1
  217. package/src/utils/terminal-width.ts +52 -0
  218. package/src/version.ts +1 -1
  219. package/src/panels/agent-inspector-panel.ts +0 -786
  220. package/src/panels/approval-panel.ts +0 -252
  221. package/src/panels/automation-control-panel.ts +0 -479
  222. package/src/panels/cockpit-panel.ts +0 -481
  223. package/src/panels/cockpit-read-model.ts +0 -238
  224. package/src/panels/communication-panel.ts +0 -256
  225. package/src/panels/control-plane-panel.ts +0 -470
  226. package/src/panels/debug-panel.ts +0 -615
  227. package/src/panels/docs-panel.ts +0 -384
  228. package/src/panels/eval-panel.ts +0 -627
  229. package/src/panels/file-explorer-panel.ts +0 -673
  230. package/src/panels/file-preview-panel.ts +0 -517
  231. package/src/panels/hooks-panel.ts +0 -401
  232. package/src/panels/incident-review-panel.ts +0 -406
  233. package/src/panels/intelligence-panel.ts +0 -383
  234. package/src/panels/knowledge-graph-panel.ts +0 -515
  235. package/src/panels/marketplace-panel.ts +0 -399
  236. package/src/panels/memory-panel.ts +0 -558
  237. package/src/panels/ops-control-panel.ts +0 -329
  238. package/src/panels/ops-strategy-panel.ts +0 -321
  239. package/src/panels/orchestration-panel.ts +0 -465
  240. package/src/panels/panel-list-panel.ts +0 -566
  241. package/src/panels/plan-dashboard-panel.ts +0 -707
  242. package/src/panels/policy-panel.ts +0 -517
  243. package/src/panels/project-planning-panel.ts +0 -731
  244. package/src/panels/provider-health-panel.ts +0 -678
  245. package/src/panels/provider-health-tracker.ts +0 -310
  246. package/src/panels/provider-health-views.ts +0 -567
  247. package/src/panels/qr-panel.ts +0 -280
  248. package/src/panels/remote-panel.ts +0 -534
  249. package/src/panels/routes-panel.ts +0 -241
  250. package/src/panels/sandbox-panel.ts +0 -456
  251. package/src/panels/security-panel.ts +0 -447
  252. package/src/panels/services-panel.ts +0 -329
  253. package/src/panels/session-browser-panel.ts +0 -496
  254. package/src/panels/settings-sync-panel.ts +0 -398
  255. package/src/panels/subscription-panel.ts +0 -342
  256. package/src/panels/symbol-outline-panel.ts +0 -619
  257. package/src/panels/system-messages-panel.ts +0 -364
  258. package/src/panels/tasks-panel.ts +0 -608
  259. package/src/panels/thinking-panel.ts +0 -333
  260. package/src/panels/tool-inspector-panel.ts +0 -537
  261. package/src/panels/work-plan-panel.ts +0 -540
  262. package/src/panels/worktree-panel.ts +0 -360
  263. package/src/panels/wrfc-panel.ts +0 -790
  264. package/src/renderer/agent-detail-modal.ts +0 -466
  265. package/src/renderer/live-tail-modal.ts +0 -156
  266. package/src/renderer/process-modal.ts +0 -671
  267. package/src/renderer/qr-renderer.ts +0 -120
@@ -8,9 +8,8 @@ import { truncateDisplay, getDisplayWidth } from '../utils/terminal-width.ts';
8
8
  import { GitService } from '@pellux/goodvibes-sdk/platform/git';
9
9
  import { BasePanel } from './base-panel.ts';
10
10
  import { UI_TONES, DIFF_TONES } from '../renderer/ui-primitives.ts';
11
- import { FilePreviewPanel } from './file-preview-panel.ts';
12
- import type { PanelIntegrationContext } from './types.ts';
13
11
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
12
+ import { PanelConfirmOverlay } from './panel-confirm-overlay.ts';
14
13
  import {
15
14
  buildBodyText,
16
15
  buildEmptyState,
@@ -262,14 +261,12 @@ export class DiffPanel extends BasePanel {
262
261
  private selectedFile = 0;
263
262
  private scrollOffset = 0;
264
263
 
265
- /** Set by the 'o' key; consumed by handlePanelIntegrationAction on the next dispatch. */
266
- private pendingOpenPreview = false;
267
-
268
264
  /** One-line confirmation for the 'w'/'h'/'s' self-load hotkeys — otherwise a
269
265
  * reload that produces identical content is visually indistinguishable
270
266
  * from the keypress doing nothing at all. */
271
267
  private hotkeyStatus: string | null = null;
272
268
 
269
+ public readonly confirmOverlay = new PanelConfirmOverlay(() => this.markDirty()); // armed by a command handler (e.g. /rewind); see panel-confirm-overlay.ts
273
270
  constructor(
274
271
  workingDirectory: string,
275
272
  private readonly requestRender: () => void = () => {},
@@ -502,6 +499,7 @@ export class DiffPanel extends BasePanel {
502
499
  }
503
500
 
504
501
  handleInput(key: string): boolean {
502
+ if (this.confirmOverlay.handleInput(key)) return true; // confirm takes priority (git-panel.ts:284-303 precedent)
505
503
  switch (key) {
506
504
  case 'up': this.scrollUp(); return true;
507
505
  case 'down': this.scrollDown(); return true;
@@ -519,48 +517,17 @@ export class DiffPanel extends BasePanel {
519
517
  case 'w': this.runHotkeyReload('working tree', () => this.showGitDiff()); return true;
520
518
  case 'h': this.runHotkeyReload('HEAD', () => this.showGitDiff('HEAD')); return true;
521
519
  case 's': this.runHotkeyReload('staged', () => this.showStagedDiff()); return true;
522
- case 'o': {
523
- if (!this.currentEntry()) return false;
524
- this.pendingOpenPreview = true;
525
- return true;
526
- }
527
520
  default: return false;
528
521
  }
529
522
  }
530
523
 
531
- /**
532
- * Cross-panel integration hook 'o' opens the currently selected file in
533
- * the preview panel via the same open/focus bridge FileExplorerPanel uses
534
- * (src/input/panel-integration-actions.ts), without this panel needing to
535
- * know about PanelManager pane/focus mechanics beyond what ctx exposes.
536
- */
537
- handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
538
- if (!this.pendingOpenPreview) return false;
539
- this.pendingOpenPreview = false;
540
- const entry = this.currentEntry();
541
- if (!entry) return false;
542
-
543
- const pm = ctx.panelManager;
544
- let previewPanel = pm.getPanel('preview');
545
- if (previewPanel instanceof FilePreviewPanel) {
546
- const pane = pm.getPaneOf('preview');
547
- pm.activateById('preview');
548
- if (pane) pm.focusPane(pane);
549
- } else {
550
- const targetPane: 'top' | 'bottom' = pm.isBottomPaneVisible()
551
- ? (pm.getFocusedPane() === 'top' ? 'bottom' : 'top')
552
- : 'bottom';
553
- const opened = pm.open('preview', targetPane);
554
- pm.show();
555
- pm.focusPane(targetPane);
556
- previewPanel = opened instanceof FilePreviewPanel ? opened : null;
557
- }
558
- if (previewPanel instanceof FilePreviewPanel) {
559
- previewPanel.openFile(entry.filePath);
560
- return true;
561
- }
562
- return false;
563
- }
524
+ // W6.1 (the purge): 'o' used to open the currently selected file in the
525
+ // preview panel via a staged pendingOpenPreview flag + a
526
+ // handlePanelIntegrationAction cross-panel hook (the same bridge
527
+ // FileExplorerPanel used). 'preview' is DELETE-disposition with no
528
+ // successor surface (no file-picker-overlay preview to repoint to
529
+ // either — verified), so the key and the hook were removed rather than
530
+ // repointed; diff no longer has an 'o' action.
564
531
 
565
532
  private scrollUp(): void {
566
533
  if (this.scrollOffset > 0) {
@@ -618,6 +585,8 @@ export class DiffPanel extends BasePanel {
618
585
  return this.trackedRender(() => {
619
586
  if (height <= 0 || width <= 0) return [];
620
587
 
588
+ const confirmLines = this.confirmOverlay.renderLines(width);
589
+ if (confirmLines) return buildPanelWorkspace(width, height, { title: 'Diff Workspace', palette: COLOR, sections: [{ title: 'Confirmation', lines: confirmLines }] });
621
590
  if (this.entries.length === 0) {
622
591
  return buildPanelWorkspace(width, height, {
623
592
  title: 'Diff Workspace',
@@ -0,0 +1,60 @@
1
+ /**
2
+ * EvalRegistry — holds the latest evaluation harness run state.
3
+ *
4
+ * W6.1 (the purge): extracted out of eval-panel.ts. 'eval' was
5
+ * DELETE-disposition (no surviving human surface — the evaluation harness
6
+ * is driven and reviewed via the `/eval` CLI command, input/commands/eval.ts)
7
+ * but this registry is still a live consumer of `/eval run` output, so it
8
+ * survives as a standalone read-model rather than being deleted with the
9
+ * view. See .goodvibes/audit/2026-07-04-wave6-briefs.json (W6.1).
10
+ */
11
+
12
+ import type { EvalSuiteResult, EvalGateResult } from '@/runtime/index.ts';
13
+
14
+ export class EvalRegistry {
15
+ private _suiteResults: EvalSuiteResult[] = [];
16
+ private _gateResults: EvalGateResult[] = [];
17
+ private _running = false;
18
+ private _lastRunAt: number | null = null;
19
+ private readonly _subscribers = new Set<() => void>();
20
+
21
+ push(result: EvalSuiteResult): void {
22
+ const idx = this._suiteResults.findIndex((r) => r.suite === result.suite);
23
+ if (idx >= 0) {
24
+ this._suiteResults[idx] = result;
25
+ } else {
26
+ this._suiteResults.push(result);
27
+ }
28
+ this._lastRunAt = Date.now();
29
+ this._notify();
30
+ }
31
+
32
+ pushGate(gate: EvalGateResult): void {
33
+ const idx = this._gateResults.findIndex((g) => g.suite === gate.suite);
34
+ if (idx >= 0) {
35
+ this._gateResults[idx] = gate;
36
+ } else {
37
+ this._gateResults.push(gate);
38
+ }
39
+ this._notify();
40
+ }
41
+
42
+ setRunning(running: boolean): void {
43
+ this._running = running;
44
+ this._notify();
45
+ }
46
+
47
+ isRunning(): boolean { return this._running; }
48
+ getLastRunAt(): number | null { return this._lastRunAt; }
49
+ getSuiteResults(): EvalSuiteResult[] { return this._suiteResults; }
50
+ getGateResults(): EvalGateResult[] { return this._gateResults; }
51
+
52
+ subscribe(cb: () => void): () => void {
53
+ this._subscribers.add(cb);
54
+ return () => this._subscribers.delete(cb);
55
+ }
56
+
57
+ private _notify(): void {
58
+ for (const cb of this._subscribers) cb();
59
+ }
60
+ }
@@ -0,0 +1,31 @@
1
+ // ---------------------------------------------------------------------------
2
+ // fleet-deep-link.ts — DEBT-5 item 4.
3
+ //
4
+ // PanelManager.open('fleet', pane, target) hands FleetPanel a
5
+ // PanelDeepLinkTarget (a process id + optional ProcessKind) so a cross-panel
6
+ // jump (the work-plan modal's 'i'/'w' agent/WRFC-chain jumps, the docs
7
+ // modal's tools-tab Enter) can select + reveal a SPECIFIC node instead of
8
+ // just opening Fleet generically. Extracted from fleet-panel.ts (already at
9
+ // the 800-line architecture cap) so the panel stays a thin caller.
10
+ //
11
+ // `kind`, when given, must also match: ProcessNode ids are unique per the
12
+ // SDK's own contract, so this is a cheap extra honesty check, not the
13
+ // primary lookup key. Reveal is "free" once selectedIndex is set —
14
+ // ScrollableListPanel's render-time scroll-window resolution keeps the
15
+ // selected row visible, so FleetPanel.receiveDeepLink does not need its own
16
+ // scroll math.
17
+ //
18
+ // A target that isn't found (pruned, killed, never existed, or a domain —
19
+ // like the docs Tools tab's static tool definitions — that has no
20
+ // corresponding fleet node at all) resolves to -1; the caller degrades
21
+ // honestly (FleetPanel.setError('node no longer present.')) rather than
22
+ // silently landing on an unrelated row.
23
+ // ---------------------------------------------------------------------------
24
+
25
+ import type { PanelDeepLinkTarget } from './types.ts';
26
+ import type { FleetTreeRow } from './fleet-read-model.ts';
27
+
28
+ /** The row index matching `target`'s id (+ kind, if given), or -1. Pure — no I/O, no mutation. */
29
+ export function resolveFleetDeepLinkIndex(rows: readonly FleetTreeRow[], target: PanelDeepLinkTarget): number {
30
+ return rows.findIndex((row) => row.node.id === target.id && (!target.kind || row.node.kind === target.kind));
31
+ }
@@ -0,0 +1,62 @@
1
+ // ---------------------------------------------------------------------------
2
+ // fleet-panel-format.ts
3
+ //
4
+ // Pure column-layout + cell-formatting helpers for FleetPanel's tree rows.
5
+ // Extracted from fleet-panel.ts (S3b hygiene) to hold that file under the
6
+ // 800-line architecture cap; these are stateless functions the panel's
7
+ // renderItem/renderDetail call, with no `this` dependency.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ import type { ProcessCostState, ProcessUsage } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
11
+ import { formatAgentCost } from './agent-inspector-shared.ts';
12
+ import { DEFAULT_PANEL_PALETTE, type ColumnSpec, type PanelPalette } from './polish.ts';
13
+ import { fleetUsageTokens, hasFleetCost, type FleetStateTone } from './fleet-read-model.ts';
14
+
15
+ // Column widths for the tree row layout. `label` absorbs whatever width is
16
+ // left over after the fixed columns + gaps; on hostile (narrow) widths the
17
+ // trailing columns simply clip (buildAlignedRow/buildSelectablePanelLine stop
18
+ // writing once a cell would overflow the row) rather than throwing or
19
+ // wrapping — the tree stays readable, just denser.
20
+ const KIND_W = 8;
21
+ const ELAPSED_W = 7;
22
+ const TOKENS_W = 7;
23
+ const COST_W = 8;
24
+ const ACTIVITY_W = 20;
25
+ const GAP = 1;
26
+ const FIXED_W = 1 /* glyph */ + KIND_W + ELAPSED_W + TOKENS_W + COST_W + ACTIVITY_W + GAP * 6;
27
+
28
+ export function planColumns(width: number): ColumnSpec[] {
29
+ const labelWidth = Math.max(10, width - FIXED_W);
30
+ return [
31
+ { width: 1 },
32
+ { width: KIND_W },
33
+ { width: labelWidth },
34
+ { width: ELAPSED_W, align: 'right' },
35
+ { width: TOKENS_W, align: 'right' },
36
+ { width: COST_W, align: 'right' },
37
+ { width: ACTIVITY_W },
38
+ ];
39
+ }
40
+
41
+ export function toneColor(tone: FleetStateTone, palette: PanelPalette): string {
42
+ switch (tone) {
43
+ case 'active': return palette.info ?? DEFAULT_PANEL_PALETTE.info;
44
+ case 'success': return palette.good ?? DEFAULT_PANEL_PALETTE.good;
45
+ case 'failure': return palette.bad ?? DEFAULT_PANEL_PALETTE.bad;
46
+ case 'warn': return palette.warn ?? DEFAULT_PANEL_PALETTE.warn;
47
+ case 'muted': return palette.dim;
48
+ }
49
+ }
50
+
51
+ export function formatFleetTokens(usage: ProcessUsage | undefined): string {
52
+ const total = fleetUsageTokens(usage);
53
+ if (total === null) return 'n/a';
54
+ return total >= 1000 ? `${(total / 1000).toFixed(1)}k` : String(total);
55
+ }
56
+
57
+ /** Honest cost display: never a fabricated $0.00 — 'unpriced' when costState says so. */
58
+ export function formatFleetCost(costUsd: number | null | undefined, costState: ProcessCostState): string {
59
+ if (!hasFleetCost(costUsd, costState)) return 'unpriced';
60
+ const formatted = formatAgentCost(costUsd as number);
61
+ return costState === 'estimated' ? `~${formatted}` : formatted;
62
+ }
@@ -0,0 +1,48 @@
1
+ // ---------------------------------------------------------------------------
2
+ // fleet-panel-worktree-detail.ts
3
+ //
4
+ // NET-NEW helper (not moved code — it was authored here because fleet-panel.ts
5
+ // sits at the architecture check's 800-line cap, scripts/check-architecture.ts):
6
+ // a single pure helper for the Fleet panel's per-work-item detail view, so a
7
+ // new panel feature doesn't have to fight that cap inline.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ import type { WorkItem } from '@pellux/goodvibes-sdk/platform/orchestration';
11
+
12
+ /**
13
+ * Per-item worktree-isolation detail text for the Fleet panel's expanded
14
+ * node detail (fleet-panel.ts renderDetail), or null for a shared-isolation
15
+ * item (or any node that isn't a work item). Inferred from the item's OWN
16
+ * fields (worktreePath/worktreeBranch/mergeState) rather than a
17
+ * workstream-level flag — adaptWorkItem's ProcessNode.raw carries
18
+ * `{ item, workstreamId }`, not the full Workstream, and these fields are
19
+ * set ONLY when the owning workstream's isolation is 'worktree'
20
+ * (engine.ts/worktree-isolation.ts, SDK), so they are a reliable
21
+ * self-contained signal with no fleet-adapter change needed.
22
+ */
23
+ export function formatWorkItemIsolationDetail(item: WorkItem): string | null {
24
+ const isolated = item.worktreePath !== undefined || item.worktreeBranch !== undefined || item.mergeState !== undefined;
25
+ if (!isolated) return null;
26
+ switch (item.mergeState) {
27
+ case 'merged':
28
+ // Papercut sweep item 3: worktree-isolation items whose integration had
29
+ // no changes to merge (mergeState 'merged', mergeHash undefined — a
30
+ // documented cosmetic case, not an error) used to read as a bare/awkward
31
+ // "merged (nothing to merge)". "merged (no changes)" says the same thing
32
+ // in the same shape as the other merge-state phrases below.
33
+ return item.mergeHash ? `worktree — merged ${item.mergeHash.slice(0, 12)}` : 'worktree — merged (no changes)';
34
+ case 'conflict':
35
+ return 'worktree — merge-conflict (kept for inspection)';
36
+ case 'pending':
37
+ return 'worktree — merge pending';
38
+ case 'n-a':
39
+ default:
40
+ return item.worktreeKept ? 'worktree — kept' : 'worktree — not yet integrated';
41
+ }
42
+ }
43
+
44
+ /** `node.raw` for a 'work-item' ProcessNode is `{ item, workstreamId }` (unknown to the panel's own types) — this narrows without fleet-panel.ts needing its own WorkItem import at the call site. Non-work-item nodes (raw is undefined/some other shape) fall through to null. */
45
+ export function formatWorkItemIsolationDetailFromRaw(raw: unknown): string | null {
46
+ const item = (raw as { item?: WorkItem } | undefined)?.item;
47
+ return item ? formatWorkItemIsolationDetail(item) : null;
48
+ }