@covibes/zeroshot 5.4.0 → 5.4.1

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 (136) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -465
  3. package/cli/index.js +101 -5231
  4. package/package.json +16 -130
  5. package/CHANGELOG.md +0 -537
  6. package/cli/commands/providers.js +0 -149
  7. package/cli/lib/first-run.js +0 -211
  8. package/cli/lib/update-checker.js +0 -281
  9. package/cli/message-formatter-utils.js +0 -75
  10. package/cli/message-formatters-normal.js +0 -275
  11. package/cli/message-formatters-watch.js +0 -181
  12. package/cluster-templates/base-templates/debug-workflow.json +0 -375
  13. package/cluster-templates/base-templates/full-workflow.json +0 -581
  14. package/cluster-templates/base-templates/single-worker.json +0 -63
  15. package/cluster-templates/base-templates/worker-validator.json +0 -208
  16. package/cluster-templates/conductor-bootstrap.json +0 -124
  17. package/docker/zeroshot-cluster/Dockerfile +0 -152
  18. package/docker/zeroshot-cluster/pre-baked-deps.json +0 -28
  19. package/lib/completion.js +0 -174
  20. package/lib/docker-config.js +0 -220
  21. package/lib/git-remote-utils.js +0 -165
  22. package/lib/id-detector.js +0 -56
  23. package/lib/provider-defaults.js +0 -62
  24. package/lib/provider-detection.js +0 -59
  25. package/lib/provider-names.js +0 -57
  26. package/lib/settings/claude-auth.js +0 -78
  27. package/lib/settings.js +0 -503
  28. package/lib/stream-json-parser.js +0 -10
  29. package/scripts/fix-node-pty-permissions.js +0 -75
  30. package/scripts/record-demo.sh +0 -279
  31. package/scripts/setup-merge-queue.sh +0 -170
  32. package/scripts/test-install.sh +0 -40
  33. package/scripts/validate-templates.js +0 -100
  34. package/src/agent/agent-config.js +0 -246
  35. package/src/agent/agent-context-builder.js +0 -444
  36. package/src/agent/agent-hook-executor.js +0 -599
  37. package/src/agent/agent-lifecycle.js +0 -743
  38. package/src/agent/agent-stuck-detector.js +0 -256
  39. package/src/agent/agent-task-executor.js +0 -1690
  40. package/src/agent/agent-trigger-evaluator.js +0 -67
  41. package/src/agent/output-extraction.js +0 -217
  42. package/src/agent/output-reformatter.js +0 -175
  43. package/src/agent/schema-utils.js +0 -146
  44. package/src/agent-wrapper.js +0 -579
  45. package/src/agents/git-pusher-template.js +0 -285
  46. package/src/attach/attach-client.js +0 -438
  47. package/src/attach/attach-server.js +0 -543
  48. package/src/attach/index.js +0 -35
  49. package/src/attach/protocol.js +0 -220
  50. package/src/attach/ring-buffer.js +0 -121
  51. package/src/attach/socket-discovery.js +0 -267
  52. package/src/claude-task-runner.js +0 -574
  53. package/src/config-router.js +0 -80
  54. package/src/config-validator.js +0 -1925
  55. package/src/copy-worker.js +0 -43
  56. package/src/input-helpers.js +0 -65
  57. package/src/isolation-manager.js +0 -1471
  58. package/src/issue-providers/README.md +0 -305
  59. package/src/issue-providers/azure-devops-provider.js +0 -273
  60. package/src/issue-providers/base-provider.js +0 -232
  61. package/src/issue-providers/github-provider.js +0 -179
  62. package/src/issue-providers/gitlab-provider.js +0 -241
  63. package/src/issue-providers/index.js +0 -196
  64. package/src/issue-providers/jira-provider.js +0 -239
  65. package/src/ledger.js +0 -631
  66. package/src/lib/safe-exec.js +0 -88
  67. package/src/logic-engine.js +0 -223
  68. package/src/message-bus-bridge.js +0 -139
  69. package/src/message-bus.js +0 -249
  70. package/src/name-generator.js +0 -232
  71. package/src/orchestrator.js +0 -2815
  72. package/src/preflight.js +0 -639
  73. package/src/process-metrics.js +0 -608
  74. package/src/providers/anthropic/cli-builder.js +0 -73
  75. package/src/providers/anthropic/index.js +0 -204
  76. package/src/providers/anthropic/models.js +0 -23
  77. package/src/providers/anthropic/output-parser.js +0 -177
  78. package/src/providers/base-provider.js +0 -251
  79. package/src/providers/capabilities.js +0 -60
  80. package/src/providers/google/cli-builder.js +0 -55
  81. package/src/providers/google/index.js +0 -116
  82. package/src/providers/google/models.js +0 -24
  83. package/src/providers/google/output-parser.js +0 -101
  84. package/src/providers/index.js +0 -91
  85. package/src/providers/openai/cli-builder.js +0 -133
  86. package/src/providers/openai/index.js +0 -136
  87. package/src/providers/openai/models.js +0 -21
  88. package/src/providers/openai/output-parser.js +0 -143
  89. package/src/providers/opencode/cli-builder.js +0 -42
  90. package/src/providers/opencode/index.js +0 -103
  91. package/src/providers/opencode/models.js +0 -55
  92. package/src/providers/opencode/output-parser.js +0 -122
  93. package/src/schemas/sub-cluster.js +0 -185
  94. package/src/status-footer.js +0 -1026
  95. package/src/sub-cluster-wrapper.js +0 -601
  96. package/src/task-runner.js +0 -30
  97. package/src/template-resolver.js +0 -372
  98. package/src/tui/CHANGES.txt +0 -133
  99. package/src/tui/LAYOUT.md +0 -261
  100. package/src/tui/README.txt +0 -192
  101. package/src/tui/TWO-LEVEL-NAVIGATION.md +0 -186
  102. package/src/tui/data-poller.js +0 -349
  103. package/src/tui/demo.js +0 -208
  104. package/src/tui/formatters.js +0 -129
  105. package/src/tui/index.js +0 -193
  106. package/src/tui/keybindings.js +0 -324
  107. package/src/tui/layout.js +0 -334
  108. package/src/tui/renderer.js +0 -184
  109. package/task-lib/attachable-watcher.js +0 -309
  110. package/task-lib/claude-recovery.js +0 -156
  111. package/task-lib/commands/clean.js +0 -50
  112. package/task-lib/commands/episodes.js +0 -105
  113. package/task-lib/commands/get-log-path.js +0 -23
  114. package/task-lib/commands/kill.js +0 -32
  115. package/task-lib/commands/list.js +0 -105
  116. package/task-lib/commands/logs.js +0 -453
  117. package/task-lib/commands/resume.js +0 -42
  118. package/task-lib/commands/run.js +0 -57
  119. package/task-lib/commands/schedule.js +0 -105
  120. package/task-lib/commands/scheduler-cmd.js +0 -96
  121. package/task-lib/commands/schedules.js +0 -148
  122. package/task-lib/commands/status.js +0 -44
  123. package/task-lib/commands/unschedule.js +0 -16
  124. package/task-lib/completion.js +0 -9
  125. package/task-lib/config.js +0 -8
  126. package/task-lib/name-generator.js +0 -230
  127. package/task-lib/package.json +0 -3
  128. package/task-lib/runner.js +0 -201
  129. package/task-lib/scheduler.js +0 -252
  130. package/task-lib/store.js +0 -529
  131. package/task-lib/tui/formatters.js +0 -215
  132. package/task-lib/tui/index.js +0 -197
  133. package/task-lib/tui/layout.js +0 -111
  134. package/task-lib/tui/renderer.js +0 -129
  135. package/task-lib/tui.js +0 -425
  136. package/task-lib/watcher.js +0 -226
@@ -1,215 +0,0 @@
1
- /**
2
- * Formatting utilities for TUI display
3
- */
4
-
5
- /**
6
- * Format timestamp as human-readable relative time
7
- * @param {number} ms - Milliseconds
8
- * @returns {string} Formatted time (e.g., "2m 30s", "1h 15m")
9
- */
10
- function formatTimestamp(ms) {
11
- if (!ms || ms < 0) return '-';
12
-
13
- const seconds = Math.floor(ms / 1000);
14
- const minutes = Math.floor(seconds / 60);
15
- const hours = Math.floor(minutes / 60);
16
- const days = Math.floor(hours / 24);
17
-
18
- if (days > 0) return `${days}d ${hours % 24}h`;
19
- if (hours > 0) return `${hours}h ${minutes % 60}m`;
20
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
21
- return `${seconds}s`;
22
- }
23
-
24
- /**
25
- * Format bytes as human-readable size
26
- * @param {number} bytes - Bytes
27
- * @returns {string} Formatted size (e.g., "1.5 MB", "512 KB")
28
- */
29
- function formatBytes(bytes) {
30
- if (!bytes || bytes === 0) return '0 B';
31
- if (bytes < 0) return '-';
32
-
33
- const units = ['B', 'KB', 'MB', 'GB'];
34
- let size = bytes;
35
- let unitIndex = 0;
36
-
37
- while (size >= 1024 && unitIndex < units.length - 1) {
38
- size /= 1024;
39
- unitIndex++;
40
- }
41
-
42
- return `${size.toFixed(1)} ${units[unitIndex]}`;
43
- }
44
-
45
- /**
46
- * Format CPU percentage
47
- * @param {number} cpu - CPU percentage (0-100)
48
- * @returns {string} Formatted CPU (e.g., "23.5%", "0.1%")
49
- */
50
- function formatCPU(cpu) {
51
- if (cpu === undefined || cpu === null || cpu < 0) return '0.0%';
52
- return `${cpu.toFixed(1)}%`;
53
- }
54
-
55
- /**
56
- * Get state icon and color
57
- * @param {string} state - Task state (pending, running, completed, failed, etc.)
58
- * @returns {string} Colored icon
59
- */
60
- function stateIcon(state) {
61
- const icons = {
62
- pending: '○',
63
- running: '●',
64
- completed: '✓',
65
- failed: '✗',
66
- killed: '⊗',
67
- unknown: '?',
68
- };
69
-
70
- const colors = {
71
- pending: 'gray',
72
- running: 'cyan',
73
- completed: 'green',
74
- failed: 'red',
75
- killed: 'red',
76
- unknown: 'gray',
77
- };
78
-
79
- const icon = icons[state] || icons.unknown;
80
- const color = colors[state] || colors.unknown;
81
-
82
- return `{${color}-fg}${icon}{/}`;
83
- }
84
-
85
- /**
86
- * Truncate string to max length with ellipsis
87
- * @param {string} str - String to truncate
88
- * @param {number} maxLen - Maximum length
89
- * @returns {string} Truncated string
90
- */
91
- function truncate(str, maxLen) {
92
- if (!str) return '';
93
- if (str.length <= maxLen) return str;
94
- return str.substring(0, maxLen - 1) + '…';
95
- }
96
-
97
- /**
98
- * Parse event type from Claude JSON stream
99
- * @param {string} line - Raw log line
100
- * @returns {object|null} Parsed event with type, text, toolName, error
101
- */
102
- function parseEvent(line) {
103
- const { trimmed, timestamp } = extractTimestamp(line);
104
-
105
- // Keep non-JSON lines as-is
106
- if (!trimmed.startsWith('{')) {
107
- return trimmed ? { type: 'raw', text: trimmed, timestamp } : null;
108
- }
109
-
110
- // Parse JSON events
111
- const event = safeParseEvent(trimmed);
112
- if (!event) {
113
- return null;
114
- }
115
-
116
- return parseStreamEvent(event, timestamp);
117
- }
118
-
119
- function extractTimestamp(line) {
120
- let trimmed = line.trim();
121
-
122
- // Strip timestamp prefix if present: [1234567890]{...} -> {...}
123
- const timestampMatch = trimmed.match(/^\[(\d+)\](.*)$/);
124
- let timestamp = Date.now();
125
- if (timestampMatch) {
126
- timestamp = parseInt(timestampMatch[1]);
127
- trimmed = timestampMatch[2];
128
- }
129
-
130
- return { trimmed, timestamp };
131
- }
132
-
133
- function safeParseEvent(trimmed) {
134
- try {
135
- return JSON.parse(trimmed);
136
- } catch {
137
- return null;
138
- }
139
- }
140
-
141
- function parseStreamEvent(event, timestamp) {
142
- if (event.type === 'stream_event') {
143
- return parseStreamDelta(event, timestamp);
144
- }
145
-
146
- if (event.type === 'assistant') {
147
- return parseAssistantMessage(event, timestamp);
148
- }
149
-
150
- if (event.type === 'result') {
151
- return parseResultEvent(event, timestamp);
152
- }
153
-
154
- return null;
155
- }
156
-
157
- function parseStreamDelta(event, timestamp) {
158
- const eventType = event.event?.type;
159
-
160
- if (eventType === 'content_block_delta') {
161
- return {
162
- type: 'text',
163
- text: event.event?.delta?.text || '',
164
- timestamp,
165
- };
166
- }
167
-
168
- if (eventType === 'content_block_start') {
169
- return parseToolUseEvent(event.event?.content_block, timestamp);
170
- }
171
-
172
- return null;
173
- }
174
-
175
- function parseToolUseEvent(block, timestamp) {
176
- if (block?.type === 'tool_use' && block?.name) {
177
- return {
178
- type: 'tool',
179
- toolName: block.name,
180
- timestamp,
181
- };
182
- }
183
-
184
- return null;
185
- }
186
-
187
- function parseAssistantMessage(event, timestamp) {
188
- const contentBlocks = event.message?.content;
189
- if (!Array.isArray(contentBlocks)) {
190
- return null;
191
- }
192
-
193
- let text = '';
194
- for (const content of contentBlocks) {
195
- if (content.type === 'text') {
196
- text += content.text;
197
- }
198
- }
199
-
200
- return text ? { type: 'text', text, timestamp } : null;
201
- }
202
-
203
- function parseResultEvent(event, timestamp) {
204
- if (!event.is_error) {
205
- return null;
206
- }
207
-
208
- return {
209
- type: 'error',
210
- text: event.result || 'Unknown error',
211
- timestamp,
212
- };
213
- }
214
-
215
- export { formatTimestamp, formatBytes, formatCPU, stateIcon, truncate, parseEvent };
@@ -1,197 +0,0 @@
1
- /**
2
- * TUI - Task Logs Dashboard
3
- *
4
- * Coordinates:
5
- * - Screen and layout
6
- * - Log file polling
7
- * - Rendering
8
- * - Keybindings
9
- */
10
-
11
- import blessed from 'blessed';
12
- import { createLayout } from './layout.js';
13
- import Renderer from './renderer.js';
14
- import { statSync, openSync, readSync, closeSync } from 'fs';
15
-
16
- class TaskLogsTUI {
17
- constructor(options) {
18
- this.taskId = options.taskId;
19
- this.logFile = options.logFile;
20
- this.taskInfo = options.taskInfo;
21
- this.pid = options.pid;
22
-
23
- // State
24
- this.lastSize = 0;
25
- this.pollInterval = null;
26
- this.widgets = null;
27
- this.screen = null;
28
- this.renderer = null;
29
- this.resourceStats = { cpu: 0, memory: 0 };
30
- }
31
-
32
- async start() {
33
- // Create screen
34
- this.screen = blessed.screen({
35
- smartCSR: true,
36
- title: `Task Logs: ${this.taskId}`,
37
- dockBorders: true,
38
- fullUnicode: true,
39
- });
40
-
41
- // Create layout
42
- this.widgets = createLayout(this.screen, this.taskId);
43
-
44
- // Create renderer
45
- this.renderer = new Renderer(this.widgets, this.screen);
46
-
47
- // Setup keybindings
48
- this._setupKeybindings();
49
-
50
- // Render initial task info
51
- this.renderer.renderTaskInfo(this.taskInfo, this.resourceStats);
52
-
53
- // Read existing log content
54
- await this._readExistingLogs();
55
-
56
- // Start polling for new content
57
- this._startPolling();
58
-
59
- // Initial render
60
- this.screen.render();
61
- }
62
-
63
- _setupKeybindings() {
64
- // Quit on 'q' or Ctrl+C
65
- this.screen.key(['q', 'C-c'], () => {
66
- this.exit();
67
- });
68
-
69
- // Scroll with arrow keys (blessed handles this automatically for logsBox)
70
- // Just ensure logsBox has focus
71
- this.widgets.logsBox.focus();
72
- }
73
-
74
- _readExistingLogs() {
75
- try {
76
- const fd = openSync(this.logFile, 'r');
77
- const size = statSync(this.logFile).size;
78
-
79
- if (size > 0) {
80
- const buffer = Buffer.alloc(size);
81
- readSync(fd, buffer, 0, size, 0);
82
- closeSync(fd);
83
-
84
- const content = buffer.toString();
85
- const lines = content.split('\n').filter((l) => l.trim());
86
-
87
- this.renderer.renderLogLines(lines);
88
- this.screen.render();
89
- }
90
-
91
- this.lastSize = size;
92
- } catch {
93
- // File might not exist yet
94
- this.lastSize = 0;
95
- }
96
- }
97
-
98
- _startPolling() {
99
- let noChangeCount = 0;
100
-
101
- this.pollInterval = setInterval(() => {
102
- try {
103
- const currentSize = statSync(this.logFile).size;
104
-
105
- if (currentSize > this.lastSize) {
106
- // Read new content
107
- const buffer = Buffer.alloc(currentSize - this.lastSize);
108
- const fd = openSync(this.logFile, 'r');
109
- readSync(fd, buffer, 0, buffer.length, this.lastSize);
110
- closeSync(fd);
111
-
112
- // Parse and render new lines
113
- const newContent = buffer.toString();
114
- const newLines = newContent.split('\n').filter((l) => l.trim());
115
-
116
- this.renderer.renderLogLines(newLines);
117
- this.screen.render();
118
-
119
- this.lastSize = currentSize;
120
- noChangeCount = 0;
121
- } else {
122
- noChangeCount++;
123
-
124
- // Check if process is still running after 5 seconds of no output
125
- if (noChangeCount >= 10 && this.pid && !this._isProcessRunning(this.pid)) {
126
- // Read any final content
127
- const finalSize = statSync(this.logFile).size;
128
- if (finalSize > this.lastSize) {
129
- const buffer = Buffer.alloc(finalSize - this.lastSize);
130
- const fd = openSync(this.logFile, 'r');
131
- readSync(fd, buffer, 0, buffer.length, this.lastSize);
132
- closeSync(fd);
133
-
134
- const finalContent = buffer.toString();
135
- const finalLines = finalContent.split('\n').filter((l) => l.trim());
136
-
137
- this.renderer.renderLogLines(finalLines);
138
- }
139
-
140
- // Update task status to completed
141
- this.taskInfo.status = 'completed';
142
- this.renderer.renderTaskInfo(this.taskInfo, this.resourceStats);
143
- this.screen.render();
144
-
145
- // Show completion message
146
- this.widgets.logsBox.log('{dim}--- Task completed ---{/}');
147
- this.screen.render();
148
- }
149
- }
150
-
151
- // Update resource stats periodically
152
- if (this.pid && this._isProcessRunning(this.pid)) {
153
- this.resourceStats = this._getResourceStats(this.pid);
154
- this.renderer.renderTaskInfo(this.taskInfo, this.resourceStats);
155
- this.screen.render();
156
- }
157
- } catch (error) {
158
- // File might have been deleted
159
- this.widgets.logsBox.log(`{red-fg}Error reading log: ${error.message}{/}`);
160
- this.screen.render();
161
- }
162
- }, 500); // Poll every 500ms
163
- }
164
-
165
- _isProcessRunning(pid) {
166
- if (!pid) return false;
167
-
168
- try {
169
- // Send signal 0 (no-op) to check if process exists
170
- process.kill(pid, 0);
171
- return true;
172
- } catch {
173
- return false;
174
- }
175
- }
176
-
177
- _getResourceStats(_pid) {
178
- // This is a simplified version - full implementation would use pidusage or similar
179
- // For now, return dummy data (the important part is the UI structure)
180
- return {
181
- cpu: 0,
182
- memory: 0,
183
- };
184
- }
185
-
186
- exit() {
187
- if (this.pollInterval) {
188
- clearInterval(this.pollInterval);
189
- }
190
- if (this.screen) {
191
- this.screen.destroy();
192
- }
193
- process.exit(0);
194
- }
195
- }
196
-
197
- export default TaskLogsTUI;
@@ -1,111 +0,0 @@
1
- /**
2
- * TUI Layout for Task Logs
3
- * Creates a blessed layout for monitoring a single task
4
- *
5
- * Layout:
6
- * - Top: Task info box (ID, status, runtime, CPU, memory)
7
- * - Middle: Live logs (scrollable, auto-scroll to bottom)
8
- * - Bottom: Help bar (keyboard shortcuts)
9
- */
10
-
11
- import blessed from 'blessed';
12
- import contrib from 'blessed-contrib';
13
-
14
- /**
15
- * Create task logs TUI layout
16
- * @param {blessed.screen} screen - Blessed screen instance
17
- * @param {string} taskId - Task ID being monitored
18
- * @returns {object} Layout widgets
19
- */
20
- function createLayout(screen, taskId) {
21
- // Create 20x12 grid for responsive layout
22
- const grid = new contrib.grid({ rows: 20, cols: 12, screen });
23
-
24
- // ============================================================
25
- // TASK INFO BOX (3 rows x 12 cols)
26
- // Shows: Task ID, Status, Runtime, CPU, Memory
27
- // ============================================================
28
-
29
- const taskInfoBox = grid.set(0, 0, 3, 12, blessed.box, {
30
- label: ` Task: ${taskId} `,
31
- content: '',
32
- tags: true,
33
- border: { type: 'line', fg: 'cyan' },
34
- style: {
35
- border: { fg: 'cyan' },
36
- label: { fg: 'cyan' },
37
- },
38
- padding: {
39
- left: 2,
40
- right: 2,
41
- },
42
- });
43
-
44
- // ============================================================
45
- // LIVE LOGS (15 rows x 12 cols)
46
- // Scrollable log output with auto-scroll
47
- // ============================================================
48
-
49
- const logsBox = grid.set(3, 0, 15, 12, contrib.log, {
50
- fg: 'white',
51
- label: ' Live Logs ',
52
- border: { type: 'line', fg: 'cyan' },
53
- tags: true,
54
- style: {
55
- border: { fg: 'cyan' },
56
- label: { fg: 'cyan' },
57
- text: { fg: 'white' },
58
- },
59
- scrollable: true,
60
- mouse: true,
61
- keyable: true,
62
- alwaysScroll: true,
63
- scrollbar: {
64
- ch: ' ',
65
- track: {
66
- bg: 'gray',
67
- },
68
- style: {
69
- inverse: true,
70
- },
71
- },
72
- });
73
-
74
- // ============================================================
75
- // HELP BAR (2 rows x 12 cols)
76
- // Keyboard shortcuts
77
- // ============================================================
78
-
79
- const helpBar = grid.set(18, 0, 2, 12, blessed.box, {
80
- label: ' Help ',
81
- content:
82
- '{cyan-fg}[↑/↓]{/} Scroll ' +
83
- '{cyan-fg}[PgUp/PgDn]{/} Page ' +
84
- '{cyan-fg}[Home/End]{/} Top/Bottom ' +
85
- '{cyan-fg}[q]{/} Quit',
86
- tags: true,
87
- border: { type: 'line', fg: 'cyan' },
88
- style: {
89
- border: { fg: 'cyan' },
90
- label: { fg: 'cyan' },
91
- text: { fg: 'white' },
92
- },
93
- padding: {
94
- left: 1,
95
- right: 1,
96
- },
97
- });
98
-
99
- // Focus on logs by default
100
- logsBox.focus();
101
-
102
- return {
103
- screen,
104
- grid,
105
- taskInfoBox,
106
- logsBox,
107
- helpBar,
108
- };
109
- }
110
-
111
- export { createLayout };
@@ -1,129 +0,0 @@
1
- /**
2
- * TUI Renderer for Task Logs
3
- * Transforms task data and log events into widget updates
4
- */
5
-
6
- import { formatTimestamp, formatBytes, formatCPU, stateIcon, parseEvent } from './formatters.js';
7
-
8
- class Renderer {
9
- /**
10
- * Create renderer instance
11
- * @param {object} widgets - Widget objects from layout
12
- * @param {object} screen - Blessed screen instance
13
- */
14
- constructor(widgets, screen) {
15
- if (!widgets) {
16
- throw new Error('Renderer requires widgets object from layout');
17
- }
18
- if (!screen) {
19
- throw new Error('Renderer requires screen instance');
20
- }
21
-
22
- this.widgets = widgets;
23
- this.screen = screen;
24
- }
25
-
26
- /**
27
- * Format task prompt for display (truncate if needed)
28
- * @param {string|undefined} prompt - Task prompt
29
- * @returns {string} Formatted prompt or empty string
30
- * @private
31
- */
32
- _formatTaskPrompt(prompt) {
33
- if (!prompt) return '';
34
- const truncated = prompt.length > 80 ? prompt.substring(0, 80) + '...' : prompt;
35
- return `{bold}Task:{/bold} {gray-fg}${truncated}{/}`;
36
- }
37
-
38
- /**
39
- * Render task info box
40
- * @param {object} taskInfo - Task metadata
41
- * @param {object} stats - CPU/memory stats
42
- */
43
- renderTaskInfo(taskInfo, stats = {}) {
44
- if (!taskInfo) return;
45
-
46
- const icon = stateIcon(taskInfo.status || 'unknown');
47
- const runtime = taskInfo.createdAt ? formatTimestamp(Date.now() - taskInfo.createdAt) : '-';
48
- const cpu = stats.cpu !== undefined ? formatCPU(stats.cpu) : '0.0%';
49
- const memory = stats.memory !== undefined ? formatBytes(stats.memory) : '0 B';
50
-
51
- const content = [
52
- `${icon} {bold}Status:{/bold} {white-fg}${taskInfo.status || 'unknown'}{/}`,
53
- `{bold}Runtime:{/bold} {white-fg}${runtime}{/}`,
54
- `{bold}CPU:{/bold} {white-fg}${cpu}{/} {bold}Memory:{/bold} {white-fg}${memory}{/}`,
55
- this._formatTaskPrompt(taskInfo.prompt),
56
- ]
57
- .filter(Boolean)
58
- .join(' ');
59
-
60
- if (this.widgets.taskInfoBox && this.widgets.taskInfoBox.setContent) {
61
- this.widgets.taskInfoBox.setContent(content);
62
- }
63
- }
64
-
65
- /**
66
- * Render log entry to logs widget
67
- * @param {string} line - Raw log line
68
- */
69
- renderLogLine(line) {
70
- if (!line) return;
71
-
72
- const event = parseEvent(line);
73
- if (!event) return;
74
-
75
- let logMessage = '';
76
-
77
- switch (event.type) {
78
- case 'text':
79
- // Plain text output
80
- logMessage = event.text;
81
- break;
82
-
83
- case 'tool':
84
- // Tool invocation
85
- logMessage = `{cyan-fg}[Tool: ${event.toolName}]{/}`;
86
- break;
87
-
88
- case 'error':
89
- // Error message
90
- logMessage = `{red-fg}[ERROR] ${event.text}{/}`;
91
- break;
92
-
93
- case 'raw':
94
- // Raw non-JSON line
95
- logMessage = `{gray-fg}${event.text}{/}`;
96
- break;
97
-
98
- default:
99
- return;
100
- }
101
-
102
- if (this.widgets.logsBox && this.widgets.logsBox.log) {
103
- this.widgets.logsBox.log(logMessage);
104
- }
105
- }
106
-
107
- /**
108
- * Render multiple log lines at once
109
- * @param {string[]} lines - Array of log lines
110
- */
111
- renderLogLines(lines) {
112
- if (!Array.isArray(lines)) return;
113
-
114
- for (const line of lines) {
115
- this.renderLogLine(line);
116
- }
117
- }
118
-
119
- /**
120
- * Trigger screen render
121
- */
122
- render() {
123
- if (this.screen && this.screen.render) {
124
- this.screen.render();
125
- }
126
- }
127
- }
128
-
129
- export default Renderer;