@pellux/goodvibes-tui 0.28.0 → 0.29.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 (118) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/src/input/commands/cost-runtime.ts +49 -0
  5. package/src/input/commands/operator-runtime.ts +5 -1
  6. package/src/input/commands.ts +2 -0
  7. package/src/input/handler-feed-routes.ts +12 -35
  8. package/src/input/handler-feed.ts +4 -8
  9. package/src/input/handler-shortcuts.ts +51 -0
  10. package/src/input/handler.ts +43 -6
  11. package/src/input/keybindings.ts +48 -8
  12. package/src/input/panel-integration-actions.ts +107 -1
  13. package/src/main.ts +5 -1
  14. package/src/panels/agent-inspector-panel.ts +125 -36
  15. package/src/panels/agent-inspector-shared.ts +144 -0
  16. package/src/panels/approval-panel.ts +67 -16
  17. package/src/panels/automation-control-panel.ts +368 -124
  18. package/src/panels/builtin/agent.ts +28 -37
  19. package/src/panels/builtin/development.ts +40 -32
  20. package/src/panels/builtin/knowledge.ts +15 -6
  21. package/src/panels/builtin/operations.ts +178 -117
  22. package/src/panels/builtin/session.ts +35 -9
  23. package/src/panels/builtin/shared.ts +98 -6
  24. package/src/panels/cockpit-panel.ts +232 -73
  25. package/src/panels/communication-panel.ts +58 -20
  26. package/src/panels/confirm-state.ts +8 -1
  27. package/src/panels/control-plane-panel.ts +351 -118
  28. package/src/panels/cost-tracker-panel.ts +165 -7
  29. package/src/panels/debug-panel.ts +312 -159
  30. package/src/panels/diff-panel.ts +203 -57
  31. package/src/panels/docs-panel.ts +152 -66
  32. package/src/panels/eval-panel.ts +220 -42
  33. package/src/panels/file-explorer-panel.ts +202 -122
  34. package/src/panels/file-preview-panel.ts +132 -57
  35. package/src/panels/git-panel.ts +309 -128
  36. package/src/panels/hooks-panel.ts +150 -22
  37. package/src/panels/incident-review-panel.ts +223 -35
  38. package/src/panels/index.ts +0 -4
  39. package/src/panels/intelligence-panel.ts +212 -86
  40. package/src/panels/knowledge-graph-panel.ts +461 -101
  41. package/src/panels/local-auth-panel.ts +240 -28
  42. package/src/panels/marketplace-panel.ts +193 -26
  43. package/src/panels/memory-panel.ts +78 -77
  44. package/src/panels/ops-control-panel.ts +146 -29
  45. package/src/panels/ops-strategy-panel.ts +72 -4
  46. package/src/panels/orchestration-panel.ts +231 -69
  47. package/src/panels/panel-list-panel.ts +154 -131
  48. package/src/panels/panel-manager.ts +97 -9
  49. package/src/panels/plan-dashboard-panel.ts +333 -17
  50. package/src/panels/plugins-panel.ts +184 -29
  51. package/src/panels/policy-panel.ts +210 -38
  52. package/src/panels/polish-core.ts +7 -2
  53. package/src/panels/polish.ts +23 -4
  54. package/src/panels/project-planning-answer-actions.ts +134 -0
  55. package/src/panels/project-planning-panel.ts +62 -113
  56. package/src/panels/provider-health-panel.ts +434 -518
  57. package/src/panels/provider-health-routes.ts +203 -0
  58. package/src/panels/provider-health-tracker.ts +194 -6
  59. package/src/panels/provider-health-views.ts +560 -0
  60. package/src/panels/qr-panel.ts +116 -30
  61. package/src/panels/remote-panel.ts +114 -36
  62. package/src/panels/routes-panel.ts +63 -22
  63. package/src/panels/sandbox-panel.ts +174 -42
  64. package/src/panels/scrollable-list-panel.ts +19 -135
  65. package/src/panels/security-panel.ts +133 -33
  66. package/src/panels/services-panel.ts +116 -64
  67. package/src/panels/session-browser-panel.ts +73 -2
  68. package/src/panels/session-maintenance.ts +4 -122
  69. package/src/panels/settings-sync-panel.ts +335 -72
  70. package/src/panels/skills-panel.ts +157 -89
  71. package/src/panels/subscription-panel.ts +86 -33
  72. package/src/panels/symbol-outline-panel.ts +248 -108
  73. package/src/panels/system-messages-panel.ts +114 -13
  74. package/src/panels/tasks-panel.ts +326 -139
  75. package/src/panels/thinking-panel.ts +43 -10
  76. package/src/panels/token-budget-panel.ts +194 -18
  77. package/src/panels/tool-inspector-panel.ts +144 -34
  78. package/src/panels/types.ts +40 -4
  79. package/src/panels/work-plan-panel.ts +280 -17
  80. package/src/panels/worktree-panel.ts +175 -42
  81. package/src/panels/wrfc-panel.ts +116 -24
  82. package/src/renderer/conversation-overlays.ts +25 -11
  83. package/src/renderer/footer-tips.ts +41 -0
  84. package/src/renderer/fullscreen-primitives.ts +22 -16
  85. package/src/renderer/help-overlay.ts +91 -14
  86. package/src/renderer/hint-grammar.ts +52 -0
  87. package/src/renderer/layout.ts +7 -7
  88. package/src/renderer/modal-factory.ts +23 -15
  89. package/src/renderer/model-picker-overlay.ts +21 -7
  90. package/src/renderer/overlay-box.ts +16 -10
  91. package/src/renderer/process-indicator.ts +3 -1
  92. package/src/renderer/progress.ts +5 -4
  93. package/src/renderer/search-overlay.ts +27 -6
  94. package/src/renderer/session-picker-modal.ts +6 -4
  95. package/src/renderer/settings-modal.ts +70 -10
  96. package/src/renderer/shell-surface.ts +41 -15
  97. package/src/renderer/status-glyphs.ts +11 -9
  98. package/src/renderer/theme.ts +60 -3
  99. package/src/renderer/ui-factory.ts +41 -30
  100. package/src/renderer/ui-primitives.ts +23 -2
  101. package/src/runtime/bootstrap-shell.ts +35 -18
  102. package/src/runtime/diagnostics/panels/index.ts +0 -3
  103. package/src/shell/ui-openers.ts +14 -22
  104. package/src/utils/format-duration.ts +2 -2
  105. package/src/utils/splash-lines.ts +44 -3
  106. package/src/version.ts +1 -1
  107. package/src/work-plans/work-plan-store.ts +13 -0
  108. package/src/panels/agent-logs-panel.ts +0 -635
  109. package/src/panels/agent-logs-shared.ts +0 -129
  110. package/src/panels/context-visualizer-panel.ts +0 -238
  111. package/src/panels/forensics-panel.ts +0 -378
  112. package/src/panels/provider-account-snapshot.ts +0 -259
  113. package/src/panels/provider-accounts-panel.ts +0 -255
  114. package/src/panels/provider-stats-panel.ts +0 -391
  115. package/src/panels/schedule-panel.ts +0 -365
  116. package/src/panels/watchers-panel.ts +0 -213
  117. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  118. package/src/runtime/diagnostics/panels/policy.ts +0 -177
@@ -1,5 +1,6 @@
1
1
  import type { Line } from '../types/grid.ts';
2
2
  import { ScrollableListPanel } from './scrollable-list-panel.ts';
3
+ import type { PanelIntegrationContext } from './types.ts';
3
4
  import type { UiCommunicationSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
4
5
  import { truncateDisplay } from '../utils/terminal-width.ts';
5
6
  import {
@@ -18,15 +19,9 @@ import {
18
19
  } from './polish.ts';
19
20
  import { createEmptyLine } from '../types/grid.ts';
20
21
 
21
- const C = {
22
- ...DEFAULT_PANEL_PALETTE,
23
- header: '#94a3b8',
24
- headerBg: '#1e293b',
25
- ok: '#22c55e',
26
- warn: '#eab308',
27
- error: '#ef4444',
28
- selectBg: '#0f172a',
29
- } as const;
22
+ // Base chrome only — title band, state colors, and text tokens all come
23
+ // straight from DEFAULT_PANEL_PALETTE (WO-002).
24
+ const C = DEFAULT_PANEL_PALETTE;
30
25
 
31
26
  type CommunicationRecord = UiCommunicationSnapshot['records'][number];
32
27
 
@@ -41,18 +36,31 @@ function fmtAgo(ts: number): string {
41
36
  return `${Math.floor(sec / 3600)}h`;
42
37
  }
43
38
 
39
+ // Set by handleInput (enter/o) and consumed on the very next
40
+ // handlePanelIntegrationAction dispatch of that same key — handleInput has
41
+ // no access to the panelManager/openAgentDetail callback.
42
+ type PendingCommunicationAction =
43
+ | { readonly kind: 'open-orchestration' }
44
+ | { readonly kind: 'open-agent-detail'; readonly agentId: string };
45
+
44
46
  export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord> {
45
47
  private readonly readModel?: UiReadModel<UiCommunicationSnapshot>;
46
48
  private readonly unsub: (() => void) | null;
49
+ private readonly openAgentDetail?: (agentId: string) => void;
47
50
  /** When true, the list is narrowed to blocked messages only (b toggles). */
48
51
  private blockedOnly = false;
52
+ private pendingAction: PendingCommunicationAction | null = null;
49
53
 
50
- public constructor(readModel?: UiReadModel<UiCommunicationSnapshot>) {
51
- super('communication', 'Communication', 'Y', 'monitoring');
54
+ public constructor(
55
+ readModel?: UiReadModel<UiCommunicationSnapshot>,
56
+ openAgentDetail?: (agentId: string) => void,
57
+ ) {
58
+ super('communication', 'Communication', 'Y', 'runtime-ops');
52
59
  this.showSelectionGutter = true; // I5: non-color selection affordance
53
60
  this.filterEnabled = true;
54
61
  this.filterLabel = 'Filter messages';
55
62
  this.readModel = readModel;
63
+ this.openAgentDetail = openAgentDetail;
56
64
  this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
57
65
  }
58
66
 
@@ -86,9 +94,38 @@ export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord>
86
94
  this.markDirty();
87
95
  return true;
88
96
  }
97
+ // Enter jumps to the orchestration workspace that actually produces this
98
+ // traffic (a direct panel jump, not a printed "/orchestration" signpost).
99
+ // Guarded so it doesn't hijack the filter input's commit-on-enter.
100
+ if ((key === 'enter' || key === 'return') && !this.filterActive) {
101
+ this.pendingAction = { kind: 'open-orchestration' };
102
+ return true;
103
+ }
104
+ // `o` on a blocked record opens the sender's agent detail (falling back
105
+ // to the receiver when the sender id is missing) via deps.openAgentDetail.
106
+ if (key === 'o' && !this.filterActive) {
107
+ const record = this.getSelectedItem();
108
+ if (!record || record.status !== 'blocked') return false;
109
+ const agentId = record.fromId || record.toId;
110
+ if (!agentId) return false;
111
+ this.pendingAction = { kind: 'open-agent-detail', agentId };
112
+ return true;
113
+ }
89
114
  return super.handleInput(key);
90
115
  }
91
116
 
117
+ public handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
118
+ if (!this.pendingAction) return false;
119
+ const action = this.pendingAction;
120
+ this.pendingAction = null;
121
+ if (action.kind === 'open-orchestration') {
122
+ ctx.panelManager.open('orchestration');
123
+ return true;
124
+ }
125
+ this.openAgentDetail?.(action.agentId);
126
+ return true;
127
+ }
128
+
92
129
  protected renderItem(record: CommunicationRecord, _index: number, selected: boolean, width: number): Line {
93
130
  const bg = selected ? C.selectBg : undefined;
94
131
  const badge = buildStatusBadge(statusBadgeKind(record.status), record.status)[0]!;
@@ -117,7 +154,6 @@ export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord>
117
154
  protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
118
155
  return [
119
156
  { command: '/orchestration', summary: 'review graphs and recursive agent activity that emit messages' },
120
- { command: '/communication', summary: 'reopen this workspace once the runtime emits message traffic' },
121
157
  ];
122
158
  }
123
159
 
@@ -133,7 +169,7 @@ export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord>
133
169
  width,
134
170
  ' Runtime store not wired into this panel yet.',
135
171
  'This workspace needs the live runtime store before it can show communication history and policy outcomes.',
136
- [{ command: '/communication', summary: 'reopen the workspace from the shell-owned runtime' }],
172
+ [{ command: '/orchestration', summary: 'inspect recursive routing and agent activity while this workspace is unwired' }],
137
173
  C,
138
174
  ),
139
175
  }],
@@ -159,7 +195,7 @@ export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord>
159
195
  ]),
160
196
  snapshot.totalBlocked > 0
161
197
  ? buildPanelLine(width, [[` ${snapshot.totalBlocked} message${snapshot.totalBlocked !== 1 ? 's' : ''} blocked by routing policy — press b to isolate blocked lanes.`, C.warn]])
162
- : buildGuidanceLine(width, '/orchestration', 'inspect recursive routing, message handoff, and broadcast posture', C),
198
+ : buildPanelLine(width, [[' No blocked lanes — press Enter on a record to inspect routing in the orchestration console.', C.dim]]),
163
199
  ];
164
200
 
165
201
  if (records.length === 0) {
@@ -171,14 +207,13 @@ export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord>
171
207
  }
172
208
 
173
209
  this.clampSelection();
174
- const visible = this.getVisibleItems();
175
- const selected = visible[this.selectedIndex];
210
+ const selected = this.getSelectedItem();
176
211
 
177
212
  const detailLines: Line[] = [];
178
213
  if (selected) {
179
214
  detailLines.push(...buildDetailBlock(width, `Message · ${selected.kind}`, [
180
215
  buildKeyValueLine(width, [
181
- { label: 'status', value: selected.status, valueColor: selected.status === 'blocked' ? C.error : C.ok },
216
+ { label: 'status', value: selected.status, valueColor: selected.status === 'blocked' ? C.bad : C.good },
182
217
  { label: 'scope', value: selected.scope, valueColor: C.value },
183
218
  { label: 'when', value: `${fmtAgo(selected.timestamp)} ago`, valueColor: C.dim },
184
219
  ], C),
@@ -196,15 +231,18 @@ export class CommunicationPanel extends ScrollableListPanel<CommunicationRecord>
196
231
  return this.renderList(width, height, {
197
232
  title: 'Communication Control Room',
198
233
  header: postureLines(),
199
- footer: [...detailLines, buildKeyboardHints(width, this.footerHints(true), C)],
234
+ footer: [...detailLines, buildKeyboardHints(width, this.footerHints(true, selected?.status === 'blocked'), C)],
200
235
  });
201
236
  }
202
237
 
203
238
  // Context-aware hints: filter keys reflect filter state, `b` reflects the
204
- // blocked-only toggle, and the inspect hint only appears when rows exist.
205
- private footerHints(hasRows: boolean): Array<{ keys: string; label: string }> {
239
+ // blocked-only toggle, `o` only appears on a blocked selection, and the
240
+ // inspect hint only appears when rows exist.
241
+ private footerHints(hasRows: boolean, onBlockedSelection = false): Array<{ keys: string; label: string }> {
206
242
  const hints: Array<{ keys: string; label: string }> = [];
207
243
  if (hasRows) hints.push({ keys: '↑/↓', label: 'select' });
244
+ hints.push({ keys: 'Enter', label: 'orchestration' });
245
+ if (onBlockedSelection) hints.push({ keys: 'o', label: 'sender detail' });
208
246
  hints.push({ keys: 'b', label: this.blockedOnly ? 'show all lanes' : 'blocked only' });
209
247
  if (this.filterActive) {
210
248
  hints.push({ keys: 'Esc', label: 'clear filter' });
@@ -28,6 +28,12 @@ export interface ConfirmState<T = string> {
28
28
  readonly subject: T;
29
29
  /** Human-readable label for the item being destroyed. */
30
30
  readonly label: string;
31
+ /**
32
+ * Action verb shown in the confirmation prompt (e.g. 'Cancel', 'Regenerate',
33
+ * 'Promote'). Defaults to 'Delete' so existing destructive confirms are
34
+ * unaffected; non-destructive confirms should set this for honest copy.
35
+ */
36
+ readonly verb?: string;
31
37
  }
32
38
 
33
39
  /**
@@ -61,9 +67,10 @@ export function handleConfirmInput<T = string>(
61
67
  */
62
68
  export function renderConfirmLines<T = string>(width: number, state: ConfirmState<T>): Line[] {
63
69
  const palette = DEFAULT_PANEL_PALETTE;
70
+ const verb = state.verb ?? 'Delete';
64
71
  return [
65
72
  buildPanelLine(width, [[
66
- ` Delete "${state.label}"?`,
73
+ ` ${verb} "${state.label}"?`,
67
74
  palette.warn,
68
75
  ]]),
69
76
  buildPanelLine(width, [