@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.
- package/LICENSE +1 -1
- package/README.md +8 -465
- package/cli/index.js +101 -5231
- package/package.json +16 -130
- package/CHANGELOG.md +0 -537
- package/cli/commands/providers.js +0 -149
- package/cli/lib/first-run.js +0 -211
- package/cli/lib/update-checker.js +0 -281
- package/cli/message-formatter-utils.js +0 -75
- package/cli/message-formatters-normal.js +0 -275
- package/cli/message-formatters-watch.js +0 -181
- package/cluster-templates/base-templates/debug-workflow.json +0 -375
- package/cluster-templates/base-templates/full-workflow.json +0 -581
- package/cluster-templates/base-templates/single-worker.json +0 -63
- package/cluster-templates/base-templates/worker-validator.json +0 -208
- package/cluster-templates/conductor-bootstrap.json +0 -124
- package/docker/zeroshot-cluster/Dockerfile +0 -152
- package/docker/zeroshot-cluster/pre-baked-deps.json +0 -28
- package/lib/completion.js +0 -174
- package/lib/docker-config.js +0 -220
- package/lib/git-remote-utils.js +0 -165
- package/lib/id-detector.js +0 -56
- package/lib/provider-defaults.js +0 -62
- package/lib/provider-detection.js +0 -59
- package/lib/provider-names.js +0 -57
- package/lib/settings/claude-auth.js +0 -78
- package/lib/settings.js +0 -503
- package/lib/stream-json-parser.js +0 -10
- package/scripts/fix-node-pty-permissions.js +0 -75
- package/scripts/record-demo.sh +0 -279
- package/scripts/setup-merge-queue.sh +0 -170
- package/scripts/test-install.sh +0 -40
- package/scripts/validate-templates.js +0 -100
- package/src/agent/agent-config.js +0 -246
- package/src/agent/agent-context-builder.js +0 -444
- package/src/agent/agent-hook-executor.js +0 -599
- package/src/agent/agent-lifecycle.js +0 -743
- package/src/agent/agent-stuck-detector.js +0 -256
- package/src/agent/agent-task-executor.js +0 -1690
- package/src/agent/agent-trigger-evaluator.js +0 -67
- package/src/agent/output-extraction.js +0 -217
- package/src/agent/output-reformatter.js +0 -175
- package/src/agent/schema-utils.js +0 -146
- package/src/agent-wrapper.js +0 -579
- package/src/agents/git-pusher-template.js +0 -285
- package/src/attach/attach-client.js +0 -438
- package/src/attach/attach-server.js +0 -543
- package/src/attach/index.js +0 -35
- package/src/attach/protocol.js +0 -220
- package/src/attach/ring-buffer.js +0 -121
- package/src/attach/socket-discovery.js +0 -267
- package/src/claude-task-runner.js +0 -574
- package/src/config-router.js +0 -80
- package/src/config-validator.js +0 -1925
- package/src/copy-worker.js +0 -43
- package/src/input-helpers.js +0 -65
- package/src/isolation-manager.js +0 -1471
- package/src/issue-providers/README.md +0 -305
- package/src/issue-providers/azure-devops-provider.js +0 -273
- package/src/issue-providers/base-provider.js +0 -232
- package/src/issue-providers/github-provider.js +0 -179
- package/src/issue-providers/gitlab-provider.js +0 -241
- package/src/issue-providers/index.js +0 -196
- package/src/issue-providers/jira-provider.js +0 -239
- package/src/ledger.js +0 -631
- package/src/lib/safe-exec.js +0 -88
- package/src/logic-engine.js +0 -223
- package/src/message-bus-bridge.js +0 -139
- package/src/message-bus.js +0 -249
- package/src/name-generator.js +0 -232
- package/src/orchestrator.js +0 -2815
- package/src/preflight.js +0 -639
- package/src/process-metrics.js +0 -608
- package/src/providers/anthropic/cli-builder.js +0 -73
- package/src/providers/anthropic/index.js +0 -204
- package/src/providers/anthropic/models.js +0 -23
- package/src/providers/anthropic/output-parser.js +0 -177
- package/src/providers/base-provider.js +0 -251
- package/src/providers/capabilities.js +0 -60
- package/src/providers/google/cli-builder.js +0 -55
- package/src/providers/google/index.js +0 -116
- package/src/providers/google/models.js +0 -24
- package/src/providers/google/output-parser.js +0 -101
- package/src/providers/index.js +0 -91
- package/src/providers/openai/cli-builder.js +0 -133
- package/src/providers/openai/index.js +0 -136
- package/src/providers/openai/models.js +0 -21
- package/src/providers/openai/output-parser.js +0 -143
- package/src/providers/opencode/cli-builder.js +0 -42
- package/src/providers/opencode/index.js +0 -103
- package/src/providers/opencode/models.js +0 -55
- package/src/providers/opencode/output-parser.js +0 -122
- package/src/schemas/sub-cluster.js +0 -185
- package/src/status-footer.js +0 -1026
- package/src/sub-cluster-wrapper.js +0 -601
- package/src/task-runner.js +0 -30
- package/src/template-resolver.js +0 -372
- package/src/tui/CHANGES.txt +0 -133
- package/src/tui/LAYOUT.md +0 -261
- package/src/tui/README.txt +0 -192
- package/src/tui/TWO-LEVEL-NAVIGATION.md +0 -186
- package/src/tui/data-poller.js +0 -349
- package/src/tui/demo.js +0 -208
- package/src/tui/formatters.js +0 -129
- package/src/tui/index.js +0 -193
- package/src/tui/keybindings.js +0 -324
- package/src/tui/layout.js +0 -334
- package/src/tui/renderer.js +0 -184
- package/task-lib/attachable-watcher.js +0 -309
- package/task-lib/claude-recovery.js +0 -156
- package/task-lib/commands/clean.js +0 -50
- package/task-lib/commands/episodes.js +0 -105
- package/task-lib/commands/get-log-path.js +0 -23
- package/task-lib/commands/kill.js +0 -32
- package/task-lib/commands/list.js +0 -105
- package/task-lib/commands/logs.js +0 -453
- package/task-lib/commands/resume.js +0 -42
- package/task-lib/commands/run.js +0 -57
- package/task-lib/commands/schedule.js +0 -105
- package/task-lib/commands/scheduler-cmd.js +0 -96
- package/task-lib/commands/schedules.js +0 -148
- package/task-lib/commands/status.js +0 -44
- package/task-lib/commands/unschedule.js +0 -16
- package/task-lib/completion.js +0 -9
- package/task-lib/config.js +0 -8
- package/task-lib/name-generator.js +0 -230
- package/task-lib/package.json +0 -3
- package/task-lib/runner.js +0 -201
- package/task-lib/scheduler.js +0 -252
- package/task-lib/store.js +0 -529
- package/task-lib/tui/formatters.js +0 -215
- package/task-lib/tui/index.js +0 -197
- package/task-lib/tui/layout.js +0 -111
- package/task-lib/tui/renderer.js +0 -129
- package/task-lib/tui.js +0 -425
- 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
|
-
}
|
package/task-lib/commands/run.js
DELETED
|
@@ -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
|
-
}
|