@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,105 +0,0 @@
1
- import chalk from 'chalk';
2
- import { loadTasks } from '../store.js';
3
- import { isProcessRunning } from '../runner.js';
4
-
5
- export function listTasks(options = {}) {
6
- const tasks = loadTasks();
7
- const taskList = Object.values(tasks);
8
-
9
- if (taskList.length === 0) {
10
- console.log(chalk.dim('No tasks found.'));
11
- return;
12
- }
13
-
14
- // Sort by creation date, oldest first (chronological)
15
- taskList.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
16
-
17
- // Filter by status if specified
18
- let filtered = taskList;
19
- if (options.status) {
20
- filtered = taskList.filter((t) => t.status === options.status);
21
- }
22
-
23
- // Limit results
24
- const limit = options.limit || 20;
25
- filtered = filtered.slice(0, limit);
26
-
27
- // Table format (default) or verbose format
28
- if (options.verbose) {
29
- // Verbose format (old behavior)
30
- console.log(chalk.bold(`\nTasks (${filtered.length}/${taskList.length})\n`));
31
-
32
- for (const task of filtered) {
33
- // Verify running status
34
- let status = task.status;
35
- if (status === 'running' && !isProcessRunning(task.pid)) {
36
- status = 'stale';
37
- }
38
-
39
- const statusColor =
40
- {
41
- running: chalk.green,
42
- completed: chalk.green,
43
- failed: chalk.red,
44
- stale: chalk.yellow,
45
- }[status] || chalk.dim;
46
-
47
- const age = getAge(task.createdAt);
48
- const timestamp = new Date(task.createdAt).toLocaleString();
49
-
50
- console.log(
51
- `${statusColor('●')} ${chalk.cyan(task.id)} ${statusColor(`[${status}]`)} ${chalk.dim(age + ' • ' + timestamp)}`
52
- );
53
- console.log(` ${chalk.dim('CWD:')} ${task.cwd}`);
54
- console.log(` ${chalk.dim('Prompt:')} ${task.prompt}`);
55
- if (task.pid && status === 'running') {
56
- console.log(` ${chalk.dim('PID:')} ${task.pid}`);
57
- }
58
- if (task.error) {
59
- console.log(` ${chalk.red('Error:')} ${task.error}`);
60
- }
61
- console.log();
62
- }
63
- } else {
64
- // Table format (clean, default)
65
- console.log(chalk.bold(`\n=== Tasks (${filtered.length}/${taskList.length}) ===`));
66
- console.log(`${'ID'.padEnd(25)} ${'Status'.padEnd(12)} ${'Age'.padEnd(10)} CWD`);
67
- console.log('-'.repeat(100));
68
-
69
- for (const task of filtered) {
70
- // Verify running status
71
- let status = task.status;
72
- if (status === 'running' && !isProcessRunning(task.pid)) {
73
- status = 'stale';
74
- }
75
-
76
- const statusColor =
77
- {
78
- running: chalk.green,
79
- completed: chalk.green,
80
- failed: chalk.red,
81
- stale: chalk.yellow,
82
- }[status] || chalk.dim;
83
-
84
- const age = getAge(task.createdAt);
85
- const cwd = task.cwd.replace(process.env.HOME, '~');
86
-
87
- console.log(
88
- `${chalk.cyan(task.id.padEnd(25))} ${statusColor(status.padEnd(12))} ${chalk.dim(age.padEnd(10))} ${chalk.dim(cwd)}`
89
- );
90
- }
91
- console.log();
92
- }
93
- }
94
-
95
- function getAge(dateStr) {
96
- const diff = Date.now() - new Date(dateStr).getTime();
97
- const mins = Math.floor(diff / 60000);
98
- const hours = Math.floor(mins / 60);
99
- const days = Math.floor(hours / 24);
100
-
101
- if (days > 0) return `${days}d ago`;
102
- if (hours > 0) return `${hours}h ago`;
103
- if (mins > 0) return `${mins}m ago`;
104
- return 'just now';
105
- }
@@ -1,453 +0,0 @@
1
- import { existsSync, statSync, readFileSync, openSync, readSync, closeSync } from 'fs';
2
- import chalk from 'chalk';
3
- import { getTask } from '../store.js';
4
- import { isProcessRunning } from '../runner.js';
5
- import { createRequire } from 'module';
6
-
7
- // Import cluster's stream parser (shared between task and cluster)
8
- const require = createRequire(import.meta.url);
9
- const { parseChunk } = require('../../lib/stream-json-parser');
10
-
11
- // Tool icons for different tool types
12
- const TOOL_ICONS = {
13
- Read: '📖',
14
- Write: '📝',
15
- Edit: '✏️',
16
- Bash: '💻',
17
- Glob: '🔍',
18
- Grep: '🔎',
19
- WebFetch: '🌐',
20
- WebSearch: '🔎',
21
- Task: '🤖',
22
- TodoWrite: '📋',
23
- AskUserQuestion: '❓',
24
- BashOutput: '📤',
25
- KillShell: '🔪',
26
- };
27
-
28
- function getToolIcon(toolName) {
29
- return TOOL_ICONS[toolName] || '🔧';
30
- }
31
-
32
- // Format tool call input for display
33
- const TOOL_CALL_FORMATTERS = {
34
- Bash: (input) => (input.command ? `$ ${input.command}` : ''),
35
- Read: (input) => formatFilePathTail(input.file_path),
36
- Write: (input) => formatFilePathTail(input.file_path, '→ '),
37
- Edit: (input) => formatFilePathTail(input.file_path),
38
- Glob: (input) => input.pattern || '',
39
- Grep: (input) => (input.pattern ? `/${input.pattern}/` : ''),
40
- WebFetch: (input) => (input.url ? input.url.substring(0, 50) : ''),
41
- WebSearch: (input) => (input.query ? `"${input.query}"` : ''),
42
- Task: (input) => input.description || '',
43
- TodoWrite: (input) => formatTodoSummary(input),
44
- AskUserQuestion: (input) => formatQuestionSummary(input),
45
- };
46
-
47
- function formatToolCall(toolName, input) {
48
- if (!input) return '';
49
-
50
- const formatter = TOOL_CALL_FORMATTERS[toolName] || formatUnknownToolCall;
51
- return formatter(input);
52
- }
53
-
54
- function formatFilePathTail(filePath, prefix = '') {
55
- if (!filePath) return '';
56
- return `${prefix}${filePath.split('/').slice(-2).join('/')}`;
57
- }
58
-
59
- function formatTodoSummary(input) {
60
- const todos = input.todos;
61
- if (!Array.isArray(todos)) return '';
62
-
63
- const statusCounts = {};
64
- for (const todo of todos) {
65
- statusCounts[todo.status] = (statusCounts[todo.status] || 0) + 1;
66
- }
67
-
68
- const parts = Object.entries(statusCounts).map(
69
- ([status, count]) => `${count} ${status.replace('_', ' ')}`
70
- );
71
-
72
- return `${todos.length} todo${todos.length === 1 ? '' : 's'} (${parts.join(', ')})`;
73
- }
74
-
75
- function formatQuestionSummary(input) {
76
- const questions = input.questions;
77
- if (!Array.isArray(questions) || questions.length === 0) {
78
- return '';
79
- }
80
-
81
- const preview = questions[0].question.substring(0, 50);
82
- const suffix = questions[0].question.length > 50 ? '...' : '';
83
- return questions.length > 1
84
- ? `${questions.length} questions: "${preview}..."`
85
- : `"${preview}${suffix}"`;
86
- }
87
-
88
- function formatUnknownToolCall(input) {
89
- const keys = Object.keys(input);
90
- if (keys.length === 0) {
91
- return '';
92
- }
93
-
94
- const value = String(input[keys[0]]);
95
- const preview = value.substring(0, 40);
96
- return preview.length < value.length ? `${preview}...` : preview;
97
- }
98
-
99
- // Format tool result for display
100
- function formatToolResult(content, isError, toolName, toolInput) {
101
- if (!content) return isError ? 'error' : 'done';
102
-
103
- // For errors, show full message
104
- if (isError) {
105
- const firstLine = content.split('\n')[0].substring(0, 80);
106
- return chalk.red(firstLine);
107
- }
108
-
109
- // For TodoWrite, show the actual todo items
110
- if (toolName === 'TodoWrite' && toolInput?.todos && Array.isArray(toolInput.todos)) {
111
- const todos = toolInput.todos;
112
- if (todos.length === 0) return chalk.dim('no todos');
113
-
114
- // Helper to get status icon
115
- const getStatusIcon = (todoStatus) => {
116
- if (todoStatus === 'completed') return '✓';
117
- if (todoStatus === 'in_progress') return '⧗';
118
- return '○';
119
- };
120
-
121
- if (todos.length === 1) {
122
- const status = getStatusIcon(todos[0].status);
123
- const suffix = todos[0].content.length > 50 ? '...' : '';
124
- return chalk.dim(`${status} ${todos[0].content.substring(0, 50)}${suffix}`);
125
- }
126
- // Multiple todos - show first one as preview
127
- const status = getStatusIcon(todos[0].status);
128
- return chalk.dim(
129
- `${status} ${todos[0].content.substring(0, 40)}... (+${todos.length - 1} more)`
130
- );
131
- }
132
-
133
- // For success, show summary
134
- const lines = content.split('\n').filter((l) => l.trim());
135
- if (lines.length === 0) return 'done';
136
- if (lines.length === 1) {
137
- const line = lines[0].substring(0, 60);
138
- return chalk.dim(line.length < lines[0].length ? line + '...' : line);
139
- }
140
- // Multiple lines - show count
141
- return chalk.dim(`${lines.length} lines`);
142
- }
143
-
144
- // Line buffer for accumulating text that streams without newlines
145
- let lineBuffer = '';
146
- let currentToolCall = null;
147
-
148
- function resetState() {
149
- lineBuffer = '';
150
- currentToolCall = null;
151
- }
152
-
153
- // Flush pending text in buffer
154
- function flushLineBuffer() {
155
- if (lineBuffer.trim()) {
156
- process.stdout.write(lineBuffer);
157
- if (!lineBuffer.endsWith('\n')) {
158
- process.stdout.write('\n');
159
- }
160
- }
161
- lineBuffer = '';
162
- }
163
-
164
- // Accumulate text and print complete lines
165
- function accumulateText(text) {
166
- lineBuffer += text;
167
-
168
- // Print complete lines, keep incomplete in buffer
169
- const lines = lineBuffer.split('\n');
170
- if (lines.length > 1) {
171
- // Print all complete lines
172
- for (let i = 0; i < lines.length - 1; i++) {
173
- console.log(lines[i]);
174
- }
175
- // Keep incomplete line in buffer
176
- lineBuffer = lines[lines.length - 1];
177
- }
178
- }
179
-
180
- // Process parsed events and output formatted content
181
- const EVENT_HANDLERS = {
182
- text: handleTextEvent,
183
- thinking: handleThinkingEvent,
184
- thinking_start: handleThinkingEvent,
185
- tool_start: handleToolStart,
186
- tool_call: handleToolCall,
187
- tool_input: handleToolInput,
188
- tool_result: handleToolResult,
189
- result: handleResult,
190
- block_end: handleBlockEnd,
191
- multi: handleMulti,
192
- };
193
-
194
- function processEvent(event) {
195
- const handler = EVENT_HANDLERS[event.type];
196
- if (handler) {
197
- handler(event);
198
- }
199
- }
200
-
201
- function handleTextEvent(event) {
202
- accumulateText(event.text);
203
- }
204
-
205
- function handleThinkingEvent(event) {
206
- if (event.text) {
207
- console.log(chalk.dim.italic(event.text));
208
- return;
209
- }
210
-
211
- if (event.type === 'thinking_start') {
212
- console.log(chalk.dim.italic('💭 thinking...'));
213
- }
214
- }
215
-
216
- function handleToolStart() {
217
- flushLineBuffer();
218
- }
219
-
220
- function handleToolCall(event) {
221
- flushLineBuffer();
222
- const icon = getToolIcon(event.toolName);
223
- const toolDesc = formatToolCall(event.toolName, event.input);
224
- console.log(`${icon} ${chalk.cyan(event.toolName)} ${chalk.dim(toolDesc)}`);
225
- currentToolCall = { toolName: event.toolName, input: event.input };
226
- }
227
-
228
- function handleToolInput() {
229
- // Streaming tool input JSON - skip (shown in tool_call)
230
- }
231
-
232
- function handleToolResult(event) {
233
- const status = event.isError ? chalk.red('✗') : chalk.green('✓');
234
- const resultDesc = formatToolResult(
235
- event.content,
236
- event.isError,
237
- currentToolCall?.toolName,
238
- currentToolCall?.input
239
- );
240
- console.log(` ${status} ${resultDesc}`);
241
- }
242
-
243
- function handleResult(event) {
244
- flushLineBuffer();
245
- if (event.error) {
246
- console.log(chalk.red(`\n✗ ERROR: ${event.error}`));
247
- return;
248
- }
249
-
250
- console.log(chalk.green(`\n✓ Completed`));
251
- if (event.cost) {
252
- console.log(chalk.dim(` Cost: $${event.cost.toFixed(4)}`));
253
- }
254
- if (event.duration) {
255
- const mins = Math.floor(event.duration / 60000);
256
- const secs = Math.floor((event.duration % 60000) / 1000);
257
- console.log(chalk.dim(` Duration: ${mins}m ${secs}s`));
258
- }
259
- }
260
-
261
- function handleBlockEnd() {
262
- // Content block ended
263
- }
264
-
265
- function handleMulti(event) {
266
- if (event.events) {
267
- for (const subEvent of event.events) {
268
- processEvent(subEvent);
269
- }
270
- }
271
- }
272
-
273
- // Parse a raw log line (may have timestamp prefix)
274
- function parseLogLine(line) {
275
- let trimmed = line.trim();
276
- if (!trimmed) return [];
277
-
278
- // Strip timestamp prefix if present: [1234567890]{...} -> {...}
279
- const timestampMatch = trimmed.match(/^\[\d+\](.*)$/);
280
- if (timestampMatch) {
281
- trimmed = timestampMatch[1];
282
- }
283
-
284
- // Non-JSON lines output as-is
285
- if (!trimmed.startsWith('{')) {
286
- return [{ type: 'text', text: trimmed + '\n' }];
287
- }
288
-
289
- // Parse JSON using cluster's parser
290
- return parseChunk(trimmed);
291
- }
292
-
293
- export async function showLogs(taskId, options = {}) {
294
- const task = getTask(taskId);
295
-
296
- if (!task) {
297
- console.log(chalk.red(`Task not found: ${taskId}`));
298
- process.exit(1);
299
- }
300
-
301
- if (!existsSync(task.logFile)) {
302
- console.log(chalk.yellow('Log file not found (task may still be starting).'));
303
- return;
304
- }
305
-
306
- const lines = options.lines || 50;
307
- const follow = options.follow;
308
- const watch = options.watch;
309
-
310
- // IMPORTANT: -f should stream logs (like tail -f), -w launches interactive TUI
311
- // This prevents confusion when users expect tail-like behavior
312
- if (watch) {
313
- // Use TUI for watch mode (interactive interface)
314
- const { default: TaskLogsTUI } = await import('../tui/index.js');
315
- const tui = new TaskLogsTUI({
316
- taskId: task.id,
317
- logFile: task.logFile,
318
- taskInfo: {
319
- status: task.status,
320
- createdAt: task.createdAt,
321
- prompt: task.prompt,
322
- },
323
- pid: task.pid,
324
- });
325
- await tui.start();
326
- } else if (follow) {
327
- // Stream logs continuously (like tail -f) - show last N lines first
328
- await tailFollow(task.logFile, task.pid, lines);
329
- } else {
330
- await tailLines(task.logFile, lines);
331
- }
332
- }
333
-
334
- function parseEventsFromLines(lines) {
335
- const events = [];
336
- for (const line of lines) {
337
- events.push(...parseLogLine(line));
338
- }
339
- return events;
340
- }
341
-
342
- function renderEvents(events) {
343
- for (const event of events) {
344
- processEvent(event);
345
- }
346
- }
347
-
348
- function renderLines(lines) {
349
- for (const line of lines) {
350
- renderEvents(parseLogLine(line));
351
- }
352
- }
353
-
354
- function readFileSlice(file, start, end) {
355
- const length = Math.max(0, end - start);
356
- if (!length) return '';
357
-
358
- const buffer = Buffer.alloc(length);
359
- const fd = openSync(file, 'r');
360
- readSync(fd, buffer, 0, buffer.length, start);
361
- closeSync(fd);
362
-
363
- return buffer.toString();
364
- }
365
-
366
- function shouldStopFollowing(noChangeCount, pid) {
367
- return noChangeCount >= 10 && pid && !isProcessRunning(pid);
368
- }
369
-
370
- function handleFinalContent(file, lastSize, interval) {
371
- const finalSize = statSync(file).size;
372
- if (finalSize > lastSize) {
373
- const finalContent = readFileSlice(file, lastSize, finalSize);
374
- renderLines(finalContent.split('\n'));
375
- flushLineBuffer();
376
- }
377
-
378
- console.log(chalk.dim('\n--- Task completed ---'));
379
- clearInterval(interval);
380
- process.exit(0);
381
- }
382
-
383
- function handleNewContent(file, lastSize, currentSize) {
384
- const newContent = readFileSlice(file, lastSize, currentSize);
385
- if (newContent) {
386
- renderLines(newContent.split('\n'));
387
- flushLineBuffer();
388
- }
389
- return currentSize;
390
- }
391
-
392
- function tailLines(file, n) {
393
- resetState();
394
- const rawContent = readFileSync(file, 'utf-8');
395
- const rawLines = rawContent.split('\n');
396
-
397
- // Parse and process all events
398
- const allEvents = parseEventsFromLines(rawLines);
399
- // Tail to last n events
400
- renderEvents(allEvents.slice(-n));
401
- flushLineBuffer();
402
- }
403
-
404
- async function tailFollow(file, pid, lines = 50) {
405
- resetState();
406
- // First, output last N events (like tail -f behavior)
407
- const rawContent = readFileSync(file, 'utf-8');
408
- const rawLines = rawContent.split('\n');
409
-
410
- // Parse all events first
411
- const allEvents = parseEventsFromLines(rawLines);
412
- // Output only the last N events
413
- renderEvents(allEvents.slice(-lines));
414
- flushLineBuffer();
415
-
416
- // Poll for changes (more reliable than fs.watch)
417
- let lastSize = statSync(file).size;
418
- let noChangeCount = 0;
419
-
420
- const interval = setInterval(() => {
421
- try {
422
- const currentSize = statSync(file).size;
423
-
424
- if (currentSize > lastSize) {
425
- lastSize = handleNewContent(file, lastSize, currentSize);
426
- noChangeCount = 0;
427
- return;
428
- }
429
-
430
- noChangeCount++;
431
-
432
- // Check if process is still running after 5 seconds of no output
433
- if (shouldStopFollowing(noChangeCount, pid)) {
434
- handleFinalContent(file, lastSize, interval);
435
- }
436
- } catch (err) {
437
- // File might have been deleted
438
- console.log(chalk.red(`\nError reading log: ${err.message}`));
439
- clearInterval(interval);
440
- process.exit(1);
441
- }
442
- }, 500); // Poll every 500ms
443
-
444
- // Keep alive until Ctrl+C
445
- process.on('SIGINT', () => {
446
- clearInterval(interval);
447
- console.log(chalk.dim('\nStopped following.'));
448
- process.exit(0);
449
- });
450
-
451
- // Keep process running
452
- await new Promise(() => {});
453
- }
@@ -1,42 +0,0 @@
1
- import chalk from 'chalk';
2
- import { getTask } from '../store.js';
3
- import { spawnTask } from '../runner.js';
4
-
5
- export async function resumeTask(taskId, newPrompt) {
6
- const task = getTask(taskId);
7
-
8
- if (!task) {
9
- console.log(chalk.red(`Task not found: ${taskId}`));
10
- process.exit(1);
11
- }
12
-
13
- if (task.status === 'running') {
14
- console.log(
15
- chalk.yellow(`Task is still running. Use 'zeroshot logs -f ${taskId}' to follow output.`)
16
- );
17
- return;
18
- }
19
-
20
- const prompt = newPrompt || 'Continue from where you left off. Complete the task.';
21
-
22
- console.log(chalk.dim(`Resuming task ${taskId}...`));
23
- console.log(chalk.dim(`Original prompt: ${task.prompt}`));
24
- console.log(chalk.dim(`Resume prompt: ${prompt}`));
25
-
26
- const newTask = await spawnTask(prompt, {
27
- cwd: task.cwd,
28
- continue: true, // Use --continue to load most recent session in that directory
29
- sessionId: task.sessionId,
30
- provider: task.provider,
31
- });
32
-
33
- console.log(chalk.green(`\n✓ Resumed as new task: ${chalk.cyan(newTask.id)}`));
34
- console.log(chalk.dim(` PID: ${newTask.pid}`));
35
- console.log(chalk.dim(` Log: ${newTask.logFile}`));
36
-
37
- console.log(chalk.dim('\nCommands:'));
38
- console.log(chalk.dim(` zeroshot logs -f ${newTask.id} # Follow output`));
39
- console.log();
40
-
41
- return newTask;
42
- }
@@ -1,57 +0,0 @@
1
- import chalk from 'chalk';
2
- import { spawnTask } from '../runner.js';
3
-
4
- export async function runTask(prompt, options = {}) {
5
- if (!prompt || prompt.trim().length === 0) {
6
- console.log(chalk.red('Error: Prompt is required'));
7
- process.exit(1);
8
- }
9
-
10
- const outputFormat = options.outputFormat || 'stream-json';
11
- const jsonSchema = options.jsonSchema;
12
- const silentJsonOutput = options.silentJsonOutput || false;
13
-
14
- console.log(chalk.dim('Spawning task...'));
15
- if (options.provider) {
16
- console.log(chalk.dim(` Provider: ${options.provider}`));
17
- }
18
- if (options.model) {
19
- console.log(chalk.dim(` Model: ${options.model}`));
20
- }
21
- if (options.modelLevel) {
22
- console.log(chalk.dim(` Level: ${options.modelLevel}`));
23
- }
24
- if (jsonSchema && outputFormat === 'json') {
25
- console.log(chalk.dim(` JSON Schema: enforced`));
26
- if (silentJsonOutput) {
27
- console.log(chalk.dim(` Silent mode: log contains ONLY final JSON`));
28
- }
29
- }
30
-
31
- const task = await spawnTask(prompt, {
32
- cwd: options.cwd || process.cwd(),
33
- model: options.model,
34
- modelLevel: options.modelLevel,
35
- reasoningEffort: options.reasoningEffort,
36
- provider: options.provider,
37
- resume: options.resume,
38
- continue: options.continue,
39
- outputFormat,
40
- jsonSchema,
41
- silentJsonOutput,
42
- });
43
-
44
- console.log(chalk.green(`\n✓ Task spawned: ${chalk.cyan(task.id)}`));
45
- console.log(chalk.dim(` Log: ${task.logFile}`));
46
- console.log(chalk.dim(` CWD: ${task.cwd}`));
47
-
48
- console.log(chalk.dim('\nCommands:'));
49
- console.log(chalk.dim(` zeroshot attach ${task.id} # Attach to task (Ctrl+B d to detach)`));
50
- console.log(chalk.dim(` zeroshot logs ${task.id} # View output`));
51
- console.log(chalk.dim(` zeroshot logs -f ${task.id} # Follow output`));
52
- console.log(chalk.dim(` zeroshot status ${task.id} # Check status`));
53
- console.log(chalk.dim(` zeroshot kill ${task.id} # Stop task`));
54
- console.log();
55
-
56
- return task;
57
- }