@pellux/goodvibes-agent 1.4.4 → 1.5.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 (227) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +7 -7
  3. package/dist/package/main.js +7424 -12241
  4. package/docs/README.md +2 -2
  5. package/docs/channels-remote-and-api.md +1 -1
  6. package/docs/getting-started.md +2 -2
  7. package/docs/release-and-publishing.md +1 -1
  8. package/docs/tools-and-commands.md +5 -5
  9. package/package.json +4 -2
  10. package/release/performance-snapshot.json +2 -2
  11. package/release/release-notes.md +11 -7
  12. package/release/release-readiness.json +6 -6
  13. package/src/agent/behavior-discovery-summary.ts +4 -18
  14. package/src/agent/calendar-registry.ts +322 -0
  15. package/src/agent/competitive-feature-inventory.ts +268 -130
  16. package/src/agent/document-registry.ts +5 -1
  17. package/src/agent/email/email-service.ts +350 -0
  18. package/src/agent/email/imap-client.ts +596 -0
  19. package/src/agent/email/smtp-client.ts +453 -0
  20. package/src/agent/ics-calendar.ts +662 -0
  21. package/src/agent/ics-timezone.ts +172 -0
  22. package/src/agent/markdown-frontmatter.ts +31 -0
  23. package/src/agent/memory-safety.ts +1 -1
  24. package/src/agent/note-registry.ts +3 -9
  25. package/src/agent/persona-discovery.ts +3 -15
  26. package/src/agent/persona-registry.ts +1 -10
  27. package/src/agent/research-source-registry.ts +5 -1
  28. package/src/agent/routine-discovery.ts +3 -15
  29. package/src/agent/runtime-profile.ts +3 -0
  30. package/src/agent/skill-discovery.ts +3 -15
  31. package/src/agent/skill-draft-proposer.ts +203 -0
  32. package/src/agent/skill-draft-runner.ts +201 -0
  33. package/src/agent/skill-registry.ts +36 -1
  34. package/src/agent/skill-standard.ts +99 -0
  35. package/src/agent/vibe-file.ts +7 -22
  36. package/src/cli/completion.ts +1 -1
  37. package/src/cli/config-overrides.ts +10 -1
  38. package/src/cli/redaction.ts +17 -5
  39. package/src/config/provider-model.ts +2 -1
  40. package/src/config/secret-config.ts +13 -6
  41. package/src/core/activity-feed.ts +97 -0
  42. package/src/core/away-digest.ts +161 -0
  43. package/src/core/conversation-rendering.ts +7 -3
  44. package/src/core/conversation.ts +22 -15
  45. package/src/core/hardware-profile.ts +362 -0
  46. package/src/core/last-seen-store.ts +78 -0
  47. package/src/core/plain-language.ts +52 -0
  48. package/src/core/setup-incomplete-hint.ts +90 -0
  49. package/src/core/system-message-router.ts +38 -87
  50. package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
  51. package/src/input/agent-workspace-basic-command-editors.ts +39 -141
  52. package/src/input/agent-workspace-categories.ts +40 -125
  53. package/src/input/agent-workspace-command-editor.ts +4 -0
  54. package/src/input/agent-workspace-host-category.ts +0 -5
  55. package/src/input/agent-workspace-local-editor-submission.ts +3 -1
  56. package/src/input/agent-workspace-navigation.ts +10 -3
  57. package/src/input/agent-workspace-onboarding-actions.ts +132 -0
  58. package/src/input/agent-workspace-onboarding-categories.ts +29 -26
  59. package/src/input/agent-workspace-onboarding-finish.ts +11 -4
  60. package/src/input/agent-workspace-onboarding-state.ts +111 -0
  61. package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
  62. package/src/input/agent-workspace-profile-editors.ts +155 -0
  63. package/src/input/agent-workspace-subscription-editor.ts +7 -0
  64. package/src/input/agent-workspace-types.ts +5 -1
  65. package/src/input/agent-workspace.ts +65 -39
  66. package/src/input/command-registry.ts +18 -5
  67. package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
  68. package/src/input/commands/agent-skills-runtime.ts +60 -3
  69. package/src/input/commands/calendar-runtime.ts +209 -0
  70. package/src/input/commands/channels-runtime.ts +1 -0
  71. package/src/input/commands/command-error.ts +9 -0
  72. package/src/input/commands/compat-runtime.ts +1 -0
  73. package/src/input/commands/config.ts +1 -0
  74. package/src/input/commands/conversation-runtime.ts +1 -1
  75. package/src/input/commands/delegation-runtime.ts +5 -6
  76. package/src/input/commands/email-runtime.ts +387 -0
  77. package/src/input/commands/experience-runtime.ts +17 -4
  78. package/src/input/commands/guidance-runtime.ts +1 -1
  79. package/src/input/commands/health-runtime.ts +6 -1
  80. package/src/input/commands/knowledge-format.ts +73 -0
  81. package/src/input/commands/knowledge.ts +9 -74
  82. package/src/input/commands/local-provider-runtime.ts +2 -1
  83. package/src/input/commands/local-runtime.ts +10 -4
  84. package/src/input/commands/mcp-runtime.ts +7 -0
  85. package/src/input/commands/notify-runtime.ts +17 -1
  86. package/src/input/commands/onboarding-runtime.ts +1 -0
  87. package/src/input/commands/operator-actions-runtime.ts +1 -0
  88. package/src/input/commands/operator-runtime.ts +3 -0
  89. package/src/input/commands/personas-runtime.ts +1 -0
  90. package/src/input/commands/platform-access-runtime.ts +40 -17
  91. package/src/input/commands/product-runtime.ts +6 -1
  92. package/src/input/commands/provider-accounts-runtime.ts +3 -2
  93. package/src/input/commands/qrcode-runtime.ts +1 -0
  94. package/src/input/commands/routines-runtime.ts +1 -0
  95. package/src/input/commands/runtime-services.ts +0 -13
  96. package/src/input/commands/security-runtime.ts +1 -0
  97. package/src/input/commands/session-content.ts +1 -0
  98. package/src/input/commands/shell-core.ts +5 -2
  99. package/src/input/commands/subscription-runtime.ts +33 -9
  100. package/src/input/commands/support-bundle-runtime.ts +8 -1
  101. package/src/input/commands/tasks-runtime.ts +1 -0
  102. package/src/input/commands/tts-runtime.ts +1 -0
  103. package/src/input/commands/vibe-runtime.ts +1 -0
  104. package/src/input/commands.ts +4 -0
  105. package/src/input/feed-context-factory.ts +3 -12
  106. package/src/input/handler-command-route.ts +7 -60
  107. package/src/input/handler-feed-routes.ts +0 -194
  108. package/src/input/handler-feed.ts +2 -54
  109. package/src/input/handler-interactions.ts +0 -2
  110. package/src/input/handler-modal-stack.ts +0 -9
  111. package/src/input/handler-picker-routes.ts +24 -1
  112. package/src/input/handler-shortcuts.ts +11 -40
  113. package/src/input/handler-ui-state.ts +13 -0
  114. package/src/input/handler.ts +8 -35
  115. package/src/input/keybindings.ts +40 -17
  116. package/src/input/model-picker-local-fit.ts +130 -0
  117. package/src/input/submission-router.ts +0 -5
  118. package/src/main.ts +111 -89
  119. package/src/renderer/activity-sidebar.ts +186 -0
  120. package/src/renderer/agent-workspace-context-lines.ts +5 -48
  121. package/src/renderer/agent-workspace-style.ts +1 -1
  122. package/src/renderer/agent-workspace.ts +14 -2
  123. package/src/renderer/compositor.ts +37 -125
  124. package/src/renderer/conversation-overlays.ts +3 -3
  125. package/src/renderer/help-overlay.ts +7 -5
  126. package/src/renderer/model-workspace.ts +101 -86
  127. package/src/{panels → renderer}/polish.ts +3 -3
  128. package/src/renderer/shell-surface.ts +4 -5
  129. package/src/renderer/status-token.ts +31 -11
  130. package/src/renderer/tab-strip.ts +1 -1
  131. package/src/renderer/tool-call.ts +7 -8
  132. package/src/renderer/tool-labels.ts +92 -0
  133. package/src/renderer/ui-factory.ts +48 -96
  134. package/src/runtime/bootstrap-command-context.ts +0 -5
  135. package/src/runtime/bootstrap-command-parts.ts +6 -15
  136. package/src/runtime/bootstrap-core.ts +34 -13
  137. package/src/runtime/bootstrap-hook-bridge.ts +3 -1
  138. package/src/runtime/bootstrap-shell.ts +3 -50
  139. package/src/runtime/bootstrap.ts +3 -4
  140. package/src/runtime/context.ts +1 -1
  141. package/src/runtime/diagnostics/panels/index.ts +0 -1
  142. package/src/runtime/diagnostics/panels/policy.ts +1 -1
  143. package/src/runtime/execution-ledger.ts +16 -1
  144. package/src/runtime/index.ts +6 -1
  145. package/src/runtime/onboarding/index.ts +1 -0
  146. package/src/runtime/onboarding/onboarding-state.ts +200 -0
  147. package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
  148. package/src/runtime/services.ts +16 -4
  149. package/src/runtime/terminal-output-guard.ts +7 -0
  150. package/src/runtime/tool-permission-safety.ts +1 -1
  151. package/src/runtime/ui/model-picker/data-provider.ts +1 -1
  152. package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
  153. package/src/runtime/ui/model-picker/types.ts +2 -2
  154. package/src/runtime/ui/provider-health/data-provider.ts +3 -3
  155. package/src/runtime/ui/provider-health/types.ts +2 -2
  156. package/src/runtime/ui-services.ts +0 -2
  157. package/src/shell/agent-workspace-fullscreen.ts +0 -1
  158. package/src/shell/autonomy-surfacing.ts +272 -0
  159. package/src/shell/session-continuity-hints.ts +0 -6
  160. package/src/shell/startup-wiring.ts +221 -0
  161. package/src/shell/ui-openers.ts +18 -29
  162. package/src/tools/agent-context-policy.ts +1 -1
  163. package/src/tools/agent-harness-background-processes.ts +4 -4
  164. package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
  165. package/src/tools/agent-harness-command-runner.ts +6 -1
  166. package/src/tools/agent-harness-document-ops.ts +26 -53
  167. package/src/tools/agent-harness-execution-history.ts +1 -13
  168. package/src/tools/agent-harness-execution-posture.ts +0 -15
  169. package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
  170. package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
  171. package/src/tools/agent-harness-metadata.ts +16 -0
  172. package/src/tools/agent-harness-mode-catalog.ts +2 -9
  173. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  174. package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
  175. package/src/tools/agent-harness-tool-schema.ts +13 -14
  176. package/src/tools/agent-harness-tool.ts +27 -23
  177. package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
  178. package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
  179. package/src/tools/agent-workspace-tool.ts +2 -33
  180. package/src/utils/terminal-width.ts +9 -3
  181. package/src/version.ts +1 -1
  182. package/src/input/agent-workspace-panel-route.ts +0 -44
  183. package/src/input/panel-integration-actions.ts +0 -26
  184. package/src/panels/approval-panel.ts +0 -149
  185. package/src/panels/automation-control-panel.ts +0 -212
  186. package/src/panels/base-panel.ts +0 -254
  187. package/src/panels/builtin/agent.ts +0 -58
  188. package/src/panels/builtin/knowledge.ts +0 -26
  189. package/src/panels/builtin/operations.ts +0 -121
  190. package/src/panels/builtin/session.ts +0 -138
  191. package/src/panels/builtin/shared.ts +0 -275
  192. package/src/panels/builtin/usage.ts +0 -21
  193. package/src/panels/builtin-panels.ts +0 -23
  194. package/src/panels/confirm-state.ts +0 -61
  195. package/src/panels/context-visualizer-panel.ts +0 -204
  196. package/src/panels/cost-tracker-panel.ts +0 -444
  197. package/src/panels/docs-panel.ts +0 -285
  198. package/src/panels/index.ts +0 -25
  199. package/src/panels/knowledge-panel.ts +0 -417
  200. package/src/panels/memory-panel.ts +0 -226
  201. package/src/panels/panel-list-panel.ts +0 -464
  202. package/src/panels/panel-manager.ts +0 -570
  203. package/src/panels/provider-accounts-panel.ts +0 -233
  204. package/src/panels/provider-health-domains.ts +0 -208
  205. package/src/panels/provider-health-panel.ts +0 -720
  206. package/src/panels/provider-health-tracker.ts +0 -115
  207. package/src/panels/provider-stats-panel.ts +0 -366
  208. package/src/panels/qr-panel.ts +0 -207
  209. package/src/panels/schedule-panel.ts +0 -321
  210. package/src/panels/scrollable-list-panel.ts +0 -491
  211. package/src/panels/search-focus.ts +0 -32
  212. package/src/panels/security-panel.ts +0 -295
  213. package/src/panels/session-browser-panel.ts +0 -395
  214. package/src/panels/session-maintenance.ts +0 -125
  215. package/src/panels/subscription-panel.ts +0 -263
  216. package/src/panels/system-messages-panel.ts +0 -230
  217. package/src/panels/tasks-panel.ts +0 -344
  218. package/src/panels/thinking-panel.ts +0 -304
  219. package/src/panels/token-budget-panel.ts +0 -475
  220. package/src/panels/tool-inspector-panel.ts +0 -436
  221. package/src/panels/types.ts +0 -54
  222. package/src/renderer/panel-composite.ts +0 -158
  223. package/src/renderer/panel-tab-bar.ts +0 -69
  224. package/src/renderer/panel-workspace-bar.ts +0 -42
  225. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  226. package/src/tools/agent-harness-panel-metadata.ts +0 -211
  227. /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
@@ -1,321 +0,0 @@
1
- import { BasePanel } from './base-panel.ts';
2
- import { type Line } from '../types/grid.ts';
3
- import type { AutomationManager } from '@pellux/goodvibes-sdk/platform/automation';
4
- import type { AutomationJob } from '@pellux/goodvibes-sdk/platform/automation';
5
- import type { AutomationRun } from '@pellux/goodvibes-sdk/platform/automation';
6
- import type { AutomationScheduleDefinition } from '@pellux/goodvibes-sdk/platform/automation';
7
- import {
8
- buildEmptyState,
9
- buildPanelLine,
10
- buildPanelWorkspace,
11
- resolveScrollablePanelSection,
12
- DEFAULT_PANEL_PALETTE,
13
- type PanelWorkspaceSection,
14
- } from './polish.ts';
15
-
16
- // ---------------------------------------------------------------------------
17
- // Colors
18
- // ---------------------------------------------------------------------------
19
-
20
- const C = {
21
- header: '#00d7ff',
22
- sectionHeader: '244',
23
- enabled: '#5fd700',
24
- disabled: '#6c6c6c',
25
- selected: '#1c1c1c',
26
- selectedFg: '#ffffff',
27
- id: '238',
28
- cron: '#af87ff',
29
- prompt: '250',
30
- nextRun: '#87afff',
31
- lastRun: '244',
32
- runCount: '#ffaf00',
33
- statusRunning: '#5fd700',
34
- statusFailed: '#ff5f5f',
35
- hint: '240',
36
- } as const;
37
-
38
- // ---------------------------------------------------------------------------
39
- // View items
40
- // ---------------------------------------------------------------------------
41
-
42
- type ViewItem =
43
- | { kind: 'header' }
44
- | { kind: 'task'; task: AutomationJob; history: AutomationRun[] }
45
- | { kind: 'empty' };
46
-
47
- type ScheduleAutomationManager = Pick<
48
- AutomationManager,
49
- 'listJobs' | 'listRuns'
50
- >;
51
-
52
- function formatSchedule(schedule: AutomationScheduleDefinition): string {
53
- switch (schedule.kind) {
54
- case 'cron':
55
- return schedule.timezone ? `${schedule.expression} [${schedule.timezone}]` : schedule.expression;
56
- case 'every':
57
- return formatEveryInterval(schedule.intervalMs);
58
- case 'at':
59
- return new Date(schedule.at).toLocaleString();
60
- }
61
- }
62
-
63
- function formatEveryInterval(intervalMs: number): string {
64
- const units: ReadonlyArray<readonly [number, string]> = [
65
- [86_400_000, 'd'],
66
- [3_600_000, 'h'],
67
- [60_000, 'm'],
68
- [1_000, 's'],
69
- ];
70
- for (const [size, unit] of units) {
71
- if (intervalMs >= size && intervalMs % size === 0) {
72
- return `${intervalMs / size}${unit}`;
73
- }
74
- }
75
- return `${intervalMs}ms`;
76
- }
77
-
78
- // ---------------------------------------------------------------------------
79
- // SchedulePanel
80
- // ---------------------------------------------------------------------------
81
-
82
- /**
83
- * SchedulePanel — displays connected schedule posture with next run time and run history.
84
- * It does not create, enable, disable, or run schedules from panel keypresses.
85
- */
86
- export class SchedulePanel extends BasePanel {
87
- private items: ViewItem[] = [];
88
- private selectedIndex = 0;
89
- private scrollOffset = 0;
90
- private readonly automationManager: ScheduleAutomationManager;
91
-
92
- constructor(automationManager: ScheduleAutomationManager) {
93
- super('schedule', 'Schedule', 'Z', 'agent');
94
- this.automationManager = automationManager;
95
- }
96
-
97
- // -------------------------------------------------------------------------
98
- // Lifecycle
99
- // -------------------------------------------------------------------------
100
-
101
- override onActivate(): void {
102
- super.onActivate();
103
- this.rebuild();
104
- }
105
-
106
- override onDeactivate(): void {
107
- this.scrollOffset = 0;
108
- }
109
-
110
- override onDestroy(): void {
111
- this.onDeactivate();
112
- super.onDestroy();
113
- }
114
-
115
- // -------------------------------------------------------------------------
116
- // Data
117
- // -------------------------------------------------------------------------
118
-
119
- private rebuild(): void {
120
- const manager = this.automationManager;
121
- const tasks = manager.listJobs();
122
-
123
- const items: ViewItem[] = [{ kind: 'header' }];
124
- if (tasks.length === 0) {
125
- items.push({ kind: 'empty' });
126
- } else {
127
- for (const task of tasks) {
128
- items.push({
129
- kind: 'task',
130
- task,
131
- history: manager.listRuns(task.id),
132
- });
133
- }
134
- }
135
- this.items = items;
136
-
137
- if (this.selectedIndex >= this.items.length) {
138
- this.selectedIndex = Math.max(0, this.items.length - 1);
139
- }
140
- }
141
-
142
- // -------------------------------------------------------------------------
143
- // Input
144
- // -------------------------------------------------------------------------
145
-
146
- handleInput(key: string): boolean {
147
- switch (key) {
148
- case 'up':
149
- case 'k': {
150
- if (this.selectedIndex > 0) {
151
- this.selectedIndex--;
152
- this.markDirty();
153
- }
154
- return true;
155
- }
156
- case 'down':
157
- case 'j': {
158
- if (this.selectedIndex < this.items.length - 1) {
159
- this.selectedIndex++;
160
- this.markDirty();
161
- }
162
- return true;
163
- }
164
- case 'return':
165
- case ' ': {
166
- this.setError('Schedule mutation is read-only in GoodVibes Agent. Schedule changes require an exact command with --yes; panel keypresses never mutate connected schedules.');
167
- return true;
168
- }
169
- case 'r': {
170
- this.setError('Schedule run is blocked in GoodVibes Agent. Use /schedule run <schedule-id> --yes only when you explicitly want to run that connected schedule.');
171
- return true;
172
- }
173
- case 'R': {
174
- // Refresh the view
175
- this.rebuild();
176
- this.markDirty();
177
- return true;
178
- }
179
- default:
180
- return false;
181
- }
182
- }
183
-
184
- // -------------------------------------------------------------------------
185
- // Rendering
186
- // -------------------------------------------------------------------------
187
-
188
- override render(width: number, height: number): Line[] {
189
- const tasks = this.automationManager.listJobs();
190
- const enabled = tasks.filter((task: AutomationJob) => task.enabled).length;
191
- if (tasks.length === 0) {
192
- return buildPanelWorkspace(width, height, {
193
- title: ' Schedule',
194
- intro: 'Review recurring scheduled tasks, next run timing, recent history, and enablement state.',
195
- sections: [
196
- {
197
- lines: buildEmptyState(
198
- width,
199
- ' No scheduled tasks',
200
- 'Schedule mutation and run controls are blocked. Use /schedule list for read-only history, or exact --yes commands for connected schedule actions.',
201
- [],
202
- DEFAULT_PANEL_PALETTE,
203
- ),
204
- },
205
- ],
206
- footerLines: [
207
- ...(this.renderErrorLine(width) ? [this.renderErrorLine(width)!] : []),
208
- buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' navigate', DEFAULT_PANEL_PALETTE.dim], [' R', DEFAULT_PANEL_PALETTE.info], [' refresh', DEFAULT_PANEL_PALETTE.dim], [' no keypress mutations', DEFAULT_PANEL_PALETTE.warn]]),
209
- ],
210
- palette: DEFAULT_PANEL_PALETTE,
211
- });
212
- }
213
-
214
- const taskItems = this.items.filter((item): item is Extract<ViewItem, { kind: 'task' }> => item.kind === 'task');
215
- this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, taskItems.length - 1));
216
- const summarySection: PanelWorkspaceSection = {
217
- title: 'Summary',
218
- lines: [
219
- buildPanelLine(width, [
220
- [' Tasks ', DEFAULT_PANEL_PALETTE.label],
221
- [String(tasks.length), DEFAULT_PANEL_PALETTE.value],
222
- [' Enabled ', DEFAULT_PANEL_PALETTE.label],
223
- [String(enabled), enabled > 0 ? DEFAULT_PANEL_PALETTE.good : DEFAULT_PANEL_PALETTE.dim],
224
- ]),
225
- ],
226
- };
227
- const scheduledTasksSection = resolveScrollablePanelSection(width, height, {
228
- intro: 'Review recurring scheduled tasks, next run timing, recent history, and enablement state.',
229
- footerLines: [
230
- ...(this.renderErrorLine(width) ? [this.renderErrorLine(width)!] : []),
231
- buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' navigate', DEFAULT_PANEL_PALETTE.dim], [' R', DEFAULT_PANEL_PALETTE.info], [' refresh', DEFAULT_PANEL_PALETTE.dim], [' no keypress mutations', DEFAULT_PANEL_PALETTE.warn]]),
232
- ],
233
- palette: DEFAULT_PANEL_PALETTE,
234
- beforeSections: [summarySection],
235
- section: {
236
- title: 'Scheduled Tasks',
237
- scrollableLines: taskItems.flatMap((item, index) => this.renderTask(item.task, item.history, index === this.selectedIndex, width)),
238
- selectedIndex: this.selectedIndex * 3,
239
- scrollOffset: this.scrollOffset,
240
- minRows: 6,
241
- },
242
- });
243
- this.scrollOffset = scheduledTasksSection.scrollOffset;
244
- const sections: PanelWorkspaceSection[] = [
245
- summarySection,
246
- scheduledTasksSection.section,
247
- ];
248
-
249
- return buildPanelWorkspace(width, height, {
250
- title: ' Schedule',
251
- intro: 'Review recurring scheduled tasks, next run timing, recent history, and enablement state.',
252
- sections,
253
- footerLines: [
254
- ...(this.renderErrorLine(width) ? [this.renderErrorLine(width)!] : []),
255
- buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' navigate', DEFAULT_PANEL_PALETTE.dim], [' R', DEFAULT_PANEL_PALETTE.info], [' refresh', DEFAULT_PANEL_PALETTE.dim], [' no keypress mutations', DEFAULT_PANEL_PALETTE.warn]]),
256
- ],
257
- palette: DEFAULT_PANEL_PALETTE,
258
- });
259
- }
260
-
261
- /**
262
- * Render a task as 3 rows:
263
- * Row 1: [status] id name schedule
264
- * Row 2: next: <date> last: <date> runs: N
265
- * Row 3: prompt preview [history]
266
- */
267
- private renderTask(task: AutomationJob, history: AutomationRun[], selected: boolean, width: number): Line[] {
268
- const bg = selected ? C.selected : undefined;
269
- const fgBase = selected ? C.selectedFg : undefined;
270
-
271
- const bullet = task.enabled ? '* ' : 'o ';
272
- const bulletFg = task.enabled ? C.enabled : C.disabled;
273
- const nameStr = task.name.length > 28 ? task.name.slice(0, 25) + '...' : task.name.padEnd(28);
274
- const scheduleText = formatSchedule(task.schedule);
275
- const row1 = buildPanelLine(width, [
276
- [bullet, bulletFg, bg],
277
- [task.id.slice(0, 12), fgBase ?? C.id, bg],
278
- [' ', fgBase ?? C.prompt, bg],
279
- [nameStr, fgBase ?? C.prompt, bg],
280
- [' ', fgBase ?? C.prompt, bg],
281
- [scheduleText, fgBase ?? C.cron, bg],
282
- ]);
283
-
284
- const indent = ' ';
285
- const nextStr = task.nextRunAt
286
- ? `next: ${new Date(task.nextRunAt).toLocaleString()}`
287
- : 'next: unknown';
288
- const lastStr = task.lastRunAt
289
- ? `last: ${new Date(task.lastRunAt).toLocaleString()}`
290
- : 'last: never';
291
- const row2 = buildPanelLine(width, [
292
- [indent, fgBase ?? C.prompt, bg],
293
- [nextStr.padEnd(36), fgBase ?? C.nextRun, bg],
294
- [lastStr.padEnd(32), fgBase ?? C.lastRun, bg],
295
- [`runs: ${task.runCount}`, fgBase ?? C.runCount, bg],
296
- ]);
297
-
298
- const maxPromptLen = Math.max(20, width - indent.length - 30);
299
- const prompt = task.execution.prompt ?? task.description ?? '';
300
- const promptPreview = prompt.length > maxPromptLen
301
- ? prompt.slice(0, maxPromptLen - 1) + '\u2026'
302
- : prompt;
303
-
304
- // Show last 3 run statuses as colored dots
305
- const recentRuns = history.slice(-3);
306
- const runSegments = recentRuns.flatMap((run) => {
307
- const dotFg = run.status === 'failed' ? C.statusFailed : C.statusRunning;
308
- return [['\u25cf', dotFg, bg] as [string, string, string?]];
309
- });
310
- const row3 = buildPanelLine(width, [
311
- [indent, fgBase ?? C.prompt, bg],
312
- [promptPreview, fgBase ?? C.prompt, bg],
313
- ...(runSegments.length > 0 ? [[' ', fgBase ?? C.prompt, bg] as [string, string, string?], ...runSegments] : []),
314
- ]);
315
-
316
- // spacer row between tasks
317
- const spacer = buildPanelLine(width, [['', fgBase ?? C.prompt, bg]]);
318
-
319
- return [row1, row2, row3, spacer];
320
- }
321
- }