@covibes/zeroshot 5.2.1 → 5.4.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.
- package/CHANGELOG.md +174 -189
- package/README.md +226 -195
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +3145 -2366
- package/cli/lib/first-run.js +40 -3
- package/cli/message-formatters-normal.js +28 -6
- package/cluster-templates/base-templates/debug-workflow.json +24 -78
- package/cluster-templates/base-templates/full-workflow.json +99 -316
- package/cluster-templates/base-templates/single-worker.json +23 -15
- package/cluster-templates/base-templates/worker-validator.json +105 -36
- package/cluster-templates/conductor-bootstrap.json +9 -7
- package/lib/docker-config.js +14 -1
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +10 -7
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +298 -15
- package/lib/stream-json-parser.js +4 -238
- package/package.json +27 -6
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/validate-templates.js +100 -0
- package/src/agent/agent-config.js +140 -63
- package/src/agent/agent-context-builder.js +336 -165
- package/src/agent/agent-hook-executor.js +337 -67
- package/src/agent/agent-lifecycle.js +386 -287
- package/src/agent/agent-stuck-detector.js +7 -7
- package/src/agent/agent-task-executor.js +944 -683
- package/src/agent/output-extraction.js +217 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent-wrapper.js +112 -31
- package/src/agents/git-pusher-template.js +285 -0
- package/src/claude-task-runner.js +145 -44
- package/src/config-router.js +13 -13
- package/src/config-validator.js +1049 -563
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +499 -320
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +273 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +179 -0
- package/src/issue-providers/gitlab-provider.js +241 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +239 -0
- package/src/ledger.js +50 -11
- package/src/lib/safe-exec.js +88 -0
- package/src/orchestrator.js +1348 -757
- package/src/preflight.js +306 -149
- package/src/process-metrics.js +98 -56
- package/src/providers/anthropic/cli-builder.js +73 -0
- package/src/providers/anthropic/index.js +204 -0
- package/src/providers/anthropic/models.js +23 -0
- package/src/providers/anthropic/output-parser.js +177 -0
- package/src/providers/base-provider.js +251 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/cli-builder.js +55 -0
- package/src/providers/google/index.js +116 -0
- package/src/providers/google/models.js +24 -0
- package/src/providers/google/output-parser.js +101 -0
- package/src/providers/index.js +91 -0
- package/src/providers/openai/cli-builder.js +133 -0
- package/src/providers/openai/index.js +136 -0
- package/src/providers/openai/models.js +21 -0
- package/src/providers/openai/output-parser.js +143 -0
- package/src/providers/opencode/cli-builder.js +42 -0
- package/src/providers/opencode/index.js +103 -0
- package/src/providers/opencode/models.js +55 -0
- package/src/providers/opencode/output-parser.js +122 -0
- package/src/schemas/sub-cluster.js +68 -39
- package/src/status-footer.js +94 -48
- package/src/sub-cluster-wrapper.js +92 -36
- package/src/task-runner.js +8 -6
- package/src/template-resolver.js +12 -9
- package/src/tui/data-poller.js +123 -99
- package/src/tui/formatters.js +4 -3
- package/src/tui/keybindings.js +259 -318
- package/src/tui/layout.js +20 -3
- package/src/tui/renderer.js +11 -21
- package/task-lib/attachable-watcher.js +150 -111
- package/task-lib/claude-recovery.js +156 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/list.js +3 -3
- package/task-lib/commands/logs.js +231 -189
- package/task-lib/commands/resume.js +3 -2
- package/task-lib/commands/run.js +12 -3
- package/task-lib/commands/schedules.js +111 -61
- package/task-lib/config.js +0 -2
- package/task-lib/runner.js +128 -50
- package/task-lib/scheduler.js +3 -3
- package/task-lib/store.js +464 -152
- package/task-lib/tui/formatters.js +94 -45
- package/task-lib/tui/renderer.js +13 -3
- package/task-lib/tui.js +73 -32
- package/task-lib/watcher.js +157 -100
- package/src/agents/git-pusher-agent.json +0 -20
- package/src/github.js +0 -103
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadTasks } from '../store.js';
|
|
3
|
+
import { isProcessRunning } from '../runner.js';
|
|
4
|
+
|
|
5
|
+
export function listEpisodes(options = {}) {
|
|
6
|
+
const tasks = loadTasks();
|
|
7
|
+
const taskList = Object.values(tasks);
|
|
8
|
+
|
|
9
|
+
if (taskList.length === 0) {
|
|
10
|
+
console.log(chalk.dim('No episodes found.'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Sort by creation date, oldest first (chronological order)
|
|
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(`\nEpisodes (${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=== Episodes (${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
|
+
}
|
|
@@ -11,8 +11,8 @@ export function listTasks(options = {}) {
|
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// Sort by creation date,
|
|
15
|
-
taskList.sort((a, b) => new Date(
|
|
14
|
+
// Sort by creation date, oldest first (chronological)
|
|
15
|
+
taskList.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
|
|
16
16
|
|
|
17
17
|
// Filter by status if specified
|
|
18
18
|
let filtered = taskList;
|
|
@@ -27,7 +27,7 @@ export function listTasks(options = {}) {
|
|
|
27
27
|
// Table format (default) or verbose format
|
|
28
28
|
if (options.verbose) {
|
|
29
29
|
// Verbose format (old behavior)
|
|
30
|
-
console.log(chalk.bold(`\
|
|
30
|
+
console.log(chalk.bold(`\nTasks (${filtered.length}/${taskList.length})\n`));
|
|
31
31
|
|
|
32
32
|
for (const task of filtered) {
|
|
33
33
|
// Verify running status
|
|
@@ -30,58 +30,70 @@ function getToolIcon(toolName) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
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
|
+
|
|
33
47
|
function formatToolCall(toolName, input) {
|
|
34
48
|
if (!input) return '';
|
|
35
49
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const keys = Object.keys(input);
|
|
79
|
-
if (keys.length > 0) {
|
|
80
|
-
const val = String(input[keys[0]]).substring(0, 40);
|
|
81
|
-
return val.length < String(input[keys[0]]).length ? val + '...' : val;
|
|
82
|
-
}
|
|
83
|
-
return '';
|
|
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 '';
|
|
84
92
|
}
|
|
93
|
+
|
|
94
|
+
const value = String(input[keys[0]]);
|
|
95
|
+
const preview = value.substring(0, 40);
|
|
96
|
+
return preview.length < value.length ? `${preview}...` : preview;
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
// Format tool result for display
|
|
@@ -98,16 +110,21 @@ function formatToolResult(content, isError, toolName, toolInput) {
|
|
|
98
110
|
if (toolName === 'TodoWrite' && toolInput?.todos && Array.isArray(toolInput.todos)) {
|
|
99
111
|
const todos = toolInput.todos;
|
|
100
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
|
+
|
|
101
121
|
if (todos.length === 1) {
|
|
102
|
-
const status =
|
|
103
|
-
|
|
104
|
-
return chalk.dim(
|
|
105
|
-
`${status} ${todos[0].content.substring(0, 50)}${todos[0].content.length > 50 ? '...' : ''}`
|
|
106
|
-
);
|
|
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}`);
|
|
107
125
|
}
|
|
108
126
|
// Multiple todos - show first one as preview
|
|
109
|
-
const status =
|
|
110
|
-
todos[0].status === 'completed' ? '✓' : todos[0].status === 'in_progress' ? '⧗' : '○';
|
|
127
|
+
const status = getStatusIcon(todos[0].status);
|
|
111
128
|
return chalk.dim(
|
|
112
129
|
`${status} ${todos[0].content.substring(0, 40)}... (+${todos.length - 1} more)`
|
|
113
130
|
);
|
|
@@ -161,77 +178,95 @@ function accumulateText(text) {
|
|
|
161
178
|
}
|
|
162
179
|
|
|
163
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
|
+
|
|
164
194
|
function processEvent(event) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
+
}
|
|
235
270
|
}
|
|
236
271
|
}
|
|
237
272
|
|
|
@@ -296,23 +331,73 @@ export async function showLogs(taskId, options = {}) {
|
|
|
296
331
|
}
|
|
297
332
|
}
|
|
298
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
|
+
|
|
299
392
|
function tailLines(file, n) {
|
|
300
393
|
resetState();
|
|
301
394
|
const rawContent = readFileSync(file, 'utf-8');
|
|
302
395
|
const rawLines = rawContent.split('\n');
|
|
303
396
|
|
|
304
397
|
// Parse and process all events
|
|
305
|
-
const allEvents =
|
|
306
|
-
for (const line of rawLines) {
|
|
307
|
-
const events = parseLogLine(line);
|
|
308
|
-
allEvents.push(...events);
|
|
309
|
-
}
|
|
310
|
-
|
|
398
|
+
const allEvents = parseEventsFromLines(rawLines);
|
|
311
399
|
// Tail to last n events
|
|
312
|
-
|
|
313
|
-
for (const event of tailedEvents) {
|
|
314
|
-
processEvent(event);
|
|
315
|
-
}
|
|
400
|
+
renderEvents(allEvents.slice(-n));
|
|
316
401
|
flushLineBuffer();
|
|
317
402
|
}
|
|
318
403
|
|
|
@@ -323,17 +408,9 @@ async function tailFollow(file, pid, lines = 50) {
|
|
|
323
408
|
const rawLines = rawContent.split('\n');
|
|
324
409
|
|
|
325
410
|
// Parse all events first
|
|
326
|
-
const allEvents =
|
|
327
|
-
for (const line of rawLines) {
|
|
328
|
-
const events = parseLogLine(line);
|
|
329
|
-
allEvents.push(...events);
|
|
330
|
-
}
|
|
331
|
-
|
|
411
|
+
const allEvents = parseEventsFromLines(rawLines);
|
|
332
412
|
// Output only the last N events
|
|
333
|
-
|
|
334
|
-
for (const event of tailedEvents) {
|
|
335
|
-
processEvent(event);
|
|
336
|
-
}
|
|
413
|
+
renderEvents(allEvents.slice(-lines));
|
|
337
414
|
flushLineBuffer();
|
|
338
415
|
|
|
339
416
|
// Poll for changes (more reliable than fs.watch)
|
|
@@ -345,51 +422,16 @@ async function tailFollow(file, pid, lines = 50) {
|
|
|
345
422
|
const currentSize = statSync(file).size;
|
|
346
423
|
|
|
347
424
|
if (currentSize > lastSize) {
|
|
348
|
-
|
|
349
|
-
const buffer = Buffer.alloc(currentSize - lastSize);
|
|
350
|
-
const fd = openSync(file, 'r');
|
|
351
|
-
readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
352
|
-
closeSync(fd);
|
|
353
|
-
|
|
354
|
-
// Parse and output new lines
|
|
355
|
-
const newLines = buffer.toString().split('\n');
|
|
356
|
-
for (const line of newLines) {
|
|
357
|
-
const events = parseLogLine(line);
|
|
358
|
-
for (const event of events) {
|
|
359
|
-
processEvent(event);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
flushLineBuffer();
|
|
363
|
-
|
|
364
|
-
lastSize = currentSize;
|
|
425
|
+
lastSize = handleNewContent(file, lastSize, currentSize);
|
|
365
426
|
noChangeCount = 0;
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const buffer = Buffer.alloc(finalSize - lastSize);
|
|
375
|
-
const fd = openSync(file, 'r');
|
|
376
|
-
readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
377
|
-
closeSync(fd);
|
|
378
|
-
|
|
379
|
-
const finalLines = buffer.toString().split('\n');
|
|
380
|
-
for (const line of finalLines) {
|
|
381
|
-
const events = parseLogLine(line);
|
|
382
|
-
for (const event of events) {
|
|
383
|
-
processEvent(event);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
flushLineBuffer();
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
console.log(chalk.dim('\n--- Task completed ---'));
|
|
390
|
-
clearInterval(interval);
|
|
391
|
-
process.exit(0);
|
|
392
|
-
}
|
|
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);
|
|
393
435
|
}
|
|
394
436
|
} catch (err) {
|
|
395
437
|
// File might have been deleted
|
|
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { getTask } from '../store.js';
|
|
3
3
|
import { spawnTask } from '../runner.js';
|
|
4
4
|
|
|
5
|
-
export function resumeTask(taskId, newPrompt) {
|
|
5
|
+
export async function resumeTask(taskId, newPrompt) {
|
|
6
6
|
const task = getTask(taskId);
|
|
7
7
|
|
|
8
8
|
if (!task) {
|
|
@@ -23,10 +23,11 @@ export function resumeTask(taskId, newPrompt) {
|
|
|
23
23
|
console.log(chalk.dim(`Original prompt: ${task.prompt}`));
|
|
24
24
|
console.log(chalk.dim(`Resume prompt: ${prompt}`));
|
|
25
25
|
|
|
26
|
-
const newTask = spawnTask(prompt, {
|
|
26
|
+
const newTask = await spawnTask(prompt, {
|
|
27
27
|
cwd: task.cwd,
|
|
28
28
|
continue: true, // Use --continue to load most recent session in that directory
|
|
29
29
|
sessionId: task.sessionId,
|
|
30
|
+
provider: task.provider,
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
console.log(chalk.green(`\n✓ Resumed as new task: ${chalk.cyan(newTask.id)}`));
|
package/task-lib/commands/run.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { spawnTask } from '../runner.js';
|
|
3
3
|
|
|
4
|
-
export function runTask(prompt, options = {}) {
|
|
4
|
+
export async function runTask(prompt, options = {}) {
|
|
5
5
|
if (!prompt || prompt.trim().length === 0) {
|
|
6
6
|
console.log(chalk.red('Error: Prompt is required'));
|
|
7
7
|
process.exit(1);
|
|
@@ -11,10 +11,16 @@ export function runTask(prompt, options = {}) {
|
|
|
11
11
|
const jsonSchema = options.jsonSchema;
|
|
12
12
|
const silentJsonOutput = options.silentJsonOutput || false;
|
|
13
13
|
|
|
14
|
-
console.log(chalk.dim('Spawning
|
|
14
|
+
console.log(chalk.dim('Spawning task...'));
|
|
15
|
+
if (options.provider) {
|
|
16
|
+
console.log(chalk.dim(` Provider: ${options.provider}`));
|
|
17
|
+
}
|
|
15
18
|
if (options.model) {
|
|
16
19
|
console.log(chalk.dim(` Model: ${options.model}`));
|
|
17
20
|
}
|
|
21
|
+
if (options.modelLevel) {
|
|
22
|
+
console.log(chalk.dim(` Level: ${options.modelLevel}`));
|
|
23
|
+
}
|
|
18
24
|
if (jsonSchema && outputFormat === 'json') {
|
|
19
25
|
console.log(chalk.dim(` JSON Schema: enforced`));
|
|
20
26
|
if (silentJsonOutput) {
|
|
@@ -22,9 +28,12 @@ export function runTask(prompt, options = {}) {
|
|
|
22
28
|
}
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
const task = spawnTask(prompt, {
|
|
31
|
+
const task = await spawnTask(prompt, {
|
|
26
32
|
cwd: options.cwd || process.cwd(),
|
|
27
33
|
model: options.model,
|
|
34
|
+
modelLevel: options.modelLevel,
|
|
35
|
+
reasoningEffort: options.reasoningEffort,
|
|
36
|
+
provider: options.provider,
|
|
28
37
|
resume: options.resume,
|
|
29
38
|
continue: options.continue,
|
|
30
39
|
outputFormat,
|