@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.
Files changed (98) hide show
  1. package/CHANGELOG.md +174 -189
  2. package/README.md +226 -195
  3. package/cli/commands/providers.js +149 -0
  4. package/cli/index.js +3145 -2366
  5. package/cli/lib/first-run.js +40 -3
  6. package/cli/message-formatters-normal.js +28 -6
  7. package/cluster-templates/base-templates/debug-workflow.json +24 -78
  8. package/cluster-templates/base-templates/full-workflow.json +99 -316
  9. package/cluster-templates/base-templates/single-worker.json +23 -15
  10. package/cluster-templates/base-templates/worker-validator.json +105 -36
  11. package/cluster-templates/conductor-bootstrap.json +9 -7
  12. package/lib/docker-config.js +14 -1
  13. package/lib/git-remote-utils.js +165 -0
  14. package/lib/id-detector.js +10 -7
  15. package/lib/provider-defaults.js +62 -0
  16. package/lib/provider-detection.js +59 -0
  17. package/lib/provider-names.js +57 -0
  18. package/lib/settings/claude-auth.js +78 -0
  19. package/lib/settings.js +298 -15
  20. package/lib/stream-json-parser.js +4 -238
  21. package/package.json +27 -6
  22. package/scripts/setup-merge-queue.sh +170 -0
  23. package/scripts/validate-templates.js +100 -0
  24. package/src/agent/agent-config.js +140 -63
  25. package/src/agent/agent-context-builder.js +336 -165
  26. package/src/agent/agent-hook-executor.js +337 -67
  27. package/src/agent/agent-lifecycle.js +386 -287
  28. package/src/agent/agent-stuck-detector.js +7 -7
  29. package/src/agent/agent-task-executor.js +944 -683
  30. package/src/agent/output-extraction.js +217 -0
  31. package/src/agent/output-reformatter.js +175 -0
  32. package/src/agent/schema-utils.js +146 -0
  33. package/src/agent-wrapper.js +112 -31
  34. package/src/agents/git-pusher-template.js +285 -0
  35. package/src/claude-task-runner.js +145 -44
  36. package/src/config-router.js +13 -13
  37. package/src/config-validator.js +1049 -563
  38. package/src/input-helpers.js +65 -0
  39. package/src/isolation-manager.js +499 -320
  40. package/src/issue-providers/README.md +305 -0
  41. package/src/issue-providers/azure-devops-provider.js +273 -0
  42. package/src/issue-providers/base-provider.js +232 -0
  43. package/src/issue-providers/github-provider.js +179 -0
  44. package/src/issue-providers/gitlab-provider.js +241 -0
  45. package/src/issue-providers/index.js +196 -0
  46. package/src/issue-providers/jira-provider.js +239 -0
  47. package/src/ledger.js +50 -11
  48. package/src/lib/safe-exec.js +88 -0
  49. package/src/orchestrator.js +1348 -757
  50. package/src/preflight.js +306 -149
  51. package/src/process-metrics.js +98 -56
  52. package/src/providers/anthropic/cli-builder.js +73 -0
  53. package/src/providers/anthropic/index.js +204 -0
  54. package/src/providers/anthropic/models.js +23 -0
  55. package/src/providers/anthropic/output-parser.js +177 -0
  56. package/src/providers/base-provider.js +251 -0
  57. package/src/providers/capabilities.js +60 -0
  58. package/src/providers/google/cli-builder.js +55 -0
  59. package/src/providers/google/index.js +116 -0
  60. package/src/providers/google/models.js +24 -0
  61. package/src/providers/google/output-parser.js +101 -0
  62. package/src/providers/index.js +91 -0
  63. package/src/providers/openai/cli-builder.js +133 -0
  64. package/src/providers/openai/index.js +136 -0
  65. package/src/providers/openai/models.js +21 -0
  66. package/src/providers/openai/output-parser.js +143 -0
  67. package/src/providers/opencode/cli-builder.js +42 -0
  68. package/src/providers/opencode/index.js +103 -0
  69. package/src/providers/opencode/models.js +55 -0
  70. package/src/providers/opencode/output-parser.js +122 -0
  71. package/src/schemas/sub-cluster.js +68 -39
  72. package/src/status-footer.js +94 -48
  73. package/src/sub-cluster-wrapper.js +92 -36
  74. package/src/task-runner.js +8 -6
  75. package/src/template-resolver.js +12 -9
  76. package/src/tui/data-poller.js +123 -99
  77. package/src/tui/formatters.js +4 -3
  78. package/src/tui/keybindings.js +259 -318
  79. package/src/tui/layout.js +20 -3
  80. package/src/tui/renderer.js +11 -21
  81. package/task-lib/attachable-watcher.js +150 -111
  82. package/task-lib/claude-recovery.js +156 -0
  83. package/task-lib/commands/episodes.js +105 -0
  84. package/task-lib/commands/list.js +3 -3
  85. package/task-lib/commands/logs.js +231 -189
  86. package/task-lib/commands/resume.js +3 -2
  87. package/task-lib/commands/run.js +12 -3
  88. package/task-lib/commands/schedules.js +111 -61
  89. package/task-lib/config.js +0 -2
  90. package/task-lib/runner.js +128 -50
  91. package/task-lib/scheduler.js +3 -3
  92. package/task-lib/store.js +464 -152
  93. package/task-lib/tui/formatters.js +94 -45
  94. package/task-lib/tui/renderer.js +13 -3
  95. package/task-lib/tui.js +73 -32
  96. package/task-lib/watcher.js +157 -100
  97. package/src/agents/git-pusher-agent.json +0 -20
  98. package/src/github.js +0 -103
@@ -10,14 +10,32 @@ export function listSchedules(_options = {}) {
10
10
  const daemonStatus = getDaemonStatus();
11
11
 
12
12
  if (scheduleList.length === 0) {
13
- console.log(chalk.dim('No schedules found.'));
14
- console.log(chalk.dim('\nCreate a schedule:'));
15
- console.log(chalk.dim(' zeroshot schedule "your prompt" --every 1h'));
16
- console.log(chalk.dim(' zeroshot schedule "your prompt" --cron "0 * * * *"'));
13
+ printNoSchedules();
17
14
  return;
18
15
  }
19
16
 
20
- // Show daemon status
17
+ printDaemonStatus(daemonStatus);
18
+
19
+ // Sort by next run time
20
+ sortSchedulesByNextRun(scheduleList);
21
+
22
+ printScheduleHeader();
23
+
24
+ for (const schedule of scheduleList) {
25
+ printSchedule(schedule);
26
+ }
27
+
28
+ console.log(chalk.dim(`Total: ${scheduleList.length} schedule(s)`));
29
+ }
30
+
31
+ function printNoSchedules() {
32
+ console.log(chalk.dim('No schedules found.'));
33
+ console.log(chalk.dim('\nCreate a schedule:'));
34
+ console.log(chalk.dim(' zeroshot schedule "your prompt" --every 1h'));
35
+ console.log(chalk.dim(' zeroshot schedule "your prompt" --cron "0 * * * *"'));
36
+ }
37
+
38
+ function printDaemonStatus(daemonStatus) {
21
39
  if (daemonStatus.running) {
22
40
  console.log(chalk.green(`Scheduler: running (PID: ${daemonStatus.pid})`));
23
41
  } else if (daemonStatus.stale) {
@@ -27,72 +45,104 @@ export function listSchedules(_options = {}) {
27
45
  console.log(chalk.dim(' Run: zeroshot scheduler start'));
28
46
  }
29
47
  console.log();
48
+ }
30
49
 
31
- // Sort by next run time
50
+ function sortSchedulesByNextRun(scheduleList) {
32
51
  scheduleList.sort((a, b) => {
33
52
  if (!a.nextRunAt) return 1;
34
53
  if (!b.nextRunAt) return -1;
35
54
  return new Date(a.nextRunAt) - new Date(b.nextRunAt);
36
55
  });
56
+ }
37
57
 
58
+ function printScheduleHeader() {
38
59
  console.log(chalk.bold('Scheduled Tasks:'));
39
60
  console.log();
61
+ }
40
62
 
41
- for (const schedule of scheduleList) {
42
- const statusColor = schedule.enabled ? chalk.green : chalk.red;
43
- const statusText = schedule.enabled ? '●' : '○';
44
-
45
- console.log(`${statusColor(statusText)} ${chalk.cyan(schedule.id)}`);
46
- console.log(chalk.dim(` Prompt: ${schedule.prompt}`));
47
-
48
- if (schedule.interval) {
49
- const ms = schedule.interval;
50
- let human;
51
- if (ms >= 7 * 24 * 60 * 60 * 1000) human = `${ms / (7 * 24 * 60 * 60 * 1000)}w`;
52
- else if (ms >= 24 * 60 * 60 * 1000) human = `${ms / (24 * 60 * 60 * 1000)}d`;
53
- else if (ms >= 60 * 60 * 1000) human = `${ms / (60 * 60 * 1000)}h`;
54
- else if (ms >= 60 * 1000) human = `${ms / (60 * 1000)}m`;
55
- else human = `${ms / 1000}s`;
56
- console.log(chalk.dim(` Interval: every ${human}`));
57
- }
58
- if (schedule.cron) {
59
- console.log(chalk.dim(` Cron: ${schedule.cron}`));
60
- }
61
-
62
- if (schedule.nextRunAt) {
63
- const nextRun = new Date(schedule.nextRunAt);
64
- const now = new Date();
65
- const diff = nextRun - now;
66
-
67
- let timeUntil;
68
- if (diff < 0) {
69
- timeUntil = 'overdue';
70
- } else if (diff < 60000) {
71
- timeUntil = 'in < 1m';
72
- } else if (diff < 3600000) {
73
- timeUntil = `in ${Math.round(diff / 60000)}m`;
74
- } else if (diff < 86400000) {
75
- timeUntil = `in ${Math.round(diff / 3600000)}h`;
76
- } else {
77
- timeUntil = `in ${Math.round(diff / 86400000)}d`;
78
- }
79
-
80
- console.log(chalk.dim(` Next run: ${nextRun.toISOString()} (${timeUntil})`));
81
- }
82
-
83
- if (schedule.lastRunAt) {
84
- console.log(chalk.dim(` Last run: ${schedule.lastRunAt}`));
85
- if (schedule.lastTaskId) {
86
- console.log(chalk.dim(` Last task: ${schedule.lastTaskId}`));
87
- }
88
- }
89
-
90
- if (schedule.verify) {
91
- console.log(chalk.dim(` Verify: enabled`));
92
- }
93
-
94
- console.log();
63
+ function printSchedule(schedule) {
64
+ printScheduleHeaderLine(schedule);
65
+ printSchedulePrompt(schedule);
66
+ printScheduleInterval(schedule);
67
+ printScheduleCron(schedule);
68
+ printScheduleNextRun(schedule);
69
+ printScheduleLastRun(schedule);
70
+ printScheduleVerify(schedule);
71
+ console.log();
72
+ }
73
+
74
+ function printScheduleHeaderLine(schedule) {
75
+ const statusColor = schedule.enabled ? chalk.green : chalk.red;
76
+ const statusText = schedule.enabled ? '●' : '○';
77
+ console.log(`${statusColor(statusText)} ${chalk.cyan(schedule.id)}`);
78
+ }
79
+
80
+ function printSchedulePrompt(schedule) {
81
+ console.log(chalk.dim(` Prompt: ${schedule.prompt}`));
82
+ }
83
+
84
+ function printScheduleInterval(schedule) {
85
+ if (!schedule.interval) {
86
+ return;
95
87
  }
88
+ const human = formatInterval(schedule.interval);
89
+ console.log(chalk.dim(` Interval: every ${human}`));
90
+ }
96
91
 
97
- console.log(chalk.dim(`Total: ${scheduleList.length} schedule(s)`));
92
+ function printScheduleCron(schedule) {
93
+ if (schedule.cron) {
94
+ console.log(chalk.dim(` Cron: ${schedule.cron}`));
95
+ }
96
+ }
97
+
98
+ function printScheduleNextRun(schedule) {
99
+ if (!schedule.nextRunAt) {
100
+ return;
101
+ }
102
+ const nextRun = new Date(schedule.nextRunAt);
103
+ const timeUntil = formatTimeUntil(nextRun);
104
+ console.log(chalk.dim(` Next run: ${nextRun.toISOString()} (${timeUntil})`));
105
+ }
106
+
107
+ function printScheduleLastRun(schedule) {
108
+ if (!schedule.lastRunAt) {
109
+ return;
110
+ }
111
+ console.log(chalk.dim(` Last run: ${schedule.lastRunAt}`));
112
+ if (schedule.lastTaskId) {
113
+ console.log(chalk.dim(` Last task: ${schedule.lastTaskId}`));
114
+ }
115
+ }
116
+
117
+ function printScheduleVerify(schedule) {
118
+ if (schedule.verify) {
119
+ console.log(chalk.dim(` Verify: enabled`));
120
+ }
121
+ }
122
+
123
+ function formatInterval(ms) {
124
+ if (ms >= 7 * 24 * 60 * 60 * 1000) return `${ms / (7 * 24 * 60 * 60 * 1000)}w`;
125
+ if (ms >= 24 * 60 * 60 * 1000) return `${ms / (24 * 60 * 60 * 1000)}d`;
126
+ if (ms >= 60 * 60 * 1000) return `${ms / (60 * 60 * 1000)}h`;
127
+ if (ms >= 60 * 1000) return `${ms / (60 * 1000)}m`;
128
+ return `${ms / 1000}s`;
129
+ }
130
+
131
+ function formatTimeUntil(nextRun) {
132
+ const now = new Date();
133
+ const diff = nextRun - now;
134
+
135
+ if (diff < 0) {
136
+ return 'overdue';
137
+ }
138
+ if (diff < 60000) {
139
+ return 'in < 1m';
140
+ }
141
+ if (diff < 3600000) {
142
+ return `in ${Math.round(diff / 60000)}m`;
143
+ }
144
+ if (diff < 86400000) {
145
+ return `in ${Math.round(diff / 3600000)}h`;
146
+ }
147
+ return `in ${Math.round(diff / 86400000)}d`;
98
148
  }
@@ -2,9 +2,7 @@ import { homedir } from 'os';
2
2
  import { join } from 'path';
3
3
 
4
4
  export const TASKS_DIR = join(homedir(), '.claude-zeroshot');
5
- export const TASKS_FILE = join(TASKS_DIR, 'tasks.json');
6
5
  export const LOGS_DIR = join(TASKS_DIR, 'logs');
7
- export const SCHEDULES_FILE = join(TASKS_DIR, 'schedules.json');
8
6
  export const SCHEDULER_PID_FILE = join(TASKS_DIR, 'scheduler.pid');
9
7
  export const SCHEDULER_LOG = join(TASKS_DIR, 'scheduler.log');
10
8
  export const DEFAULT_MODEL = 'sonnet';
@@ -1,56 +1,133 @@
1
1
  import { fork } from 'child_process';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import { LOGS_DIR, DEFAULT_MODEL } from './config.js';
4
+ import { LOGS_DIR } from './config.js';
5
5
  import { addTask, generateId, ensureDirs } from './store.js';
6
+ import { createRequire } from 'module';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const { loadSettings } = require('../lib/settings.js');
10
+ const { normalizeProviderName } = require('../lib/provider-names');
11
+ const { getProvider } = require('../src/providers');
6
12
 
7
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
14
 
9
- export function spawnTask(prompt, options = {}) {
15
+ export async function spawnTask(prompt, options = {}) {
10
16
  ensureDirs();
11
17
 
12
18
  const id = generateId();
13
19
  const logFile = join(LOGS_DIR, `${id}.log`);
14
20
  const cwd = options.cwd || process.cwd();
15
- const model = options.model || process.env.ANTHROPIC_MODEL || DEFAULT_MODEL;
16
-
17
- // Build claude command args
18
- // --print: non-interactive mode
19
- // --dangerously-skip-permissions: background tasks can't prompt for approval (CRITICAL)
20
- // --output-format: stream-json (default) for real-time, text for clean output, json for structured
21
- const outputFormat = options.outputFormat || 'stream-json';
22
- const args = ['--print', '--dangerously-skip-permissions', '--output-format', outputFormat];
23
-
24
- // Only add streaming options for stream-json format
25
- if (outputFormat === 'stream-json') {
26
- args.push('--verbose');
27
- // Include partial messages to get streaming updates before completion (required for stream-json format)
28
- args.push('--include-partial-messages');
21
+
22
+ const settings = loadSettings();
23
+ const { providerName, provider, providerSettings, levelOverrides } = resolveProviderContext(
24
+ options,
25
+ settings
26
+ );
27
+
28
+ const outputFormat = resolveOutputFormat(options);
29
+ const jsonSchema = resolveJsonSchema(options, outputFormat);
30
+ const modelSpec = resolveModelSpec(options, provider, providerSettings, levelOverrides);
31
+
32
+ const cliFeatures = await provider.getCliFeatures();
33
+ const commandSpec = provider.buildCommand(prompt, {
34
+ modelSpec,
35
+ outputFormat,
36
+ jsonSchema,
37
+ cwd,
38
+ autoApprove: true,
39
+ cliFeatures,
40
+ });
41
+
42
+ const finalArgs = resolveFinalArgs(commandSpec, providerName, options);
43
+ const task = buildTaskRecord({
44
+ id,
45
+ prompt,
46
+ cwd,
47
+ options,
48
+ logFile,
49
+ providerName,
50
+ modelSpec,
51
+ });
52
+
53
+ addTask(task);
54
+
55
+ const watcherConfig = buildWatcherConfig(
56
+ outputFormat,
57
+ jsonSchema,
58
+ options,
59
+ providerName,
60
+ commandSpec
61
+ );
62
+ const watcherScript = resolveWatcherScript(options);
63
+ spawnWatcher({
64
+ watcherScript,
65
+ id,
66
+ cwd,
67
+ logFile,
68
+ finalArgs,
69
+ watcherConfig,
70
+ });
71
+
72
+ return task;
73
+ }
74
+
75
+ function resolveProviderContext(options, settings) {
76
+ const providerName = normalizeProviderName(
77
+ options.provider || settings.defaultProvider || 'claude'
78
+ );
79
+ const provider = getProvider(providerName);
80
+ const providerSettings = settings.providerSettings?.[providerName] || {};
81
+ const levelOverrides = providerSettings.levelOverrides || {};
82
+ return { providerName, provider, providerSettings, levelOverrides };
83
+ }
84
+
85
+ function resolveOutputFormat(options) {
86
+ return options.outputFormat || 'stream-json';
87
+ }
88
+
89
+ function resolveJsonSchema(options, outputFormat) {
90
+ let jsonSchema = options.jsonSchema || null;
91
+ if (jsonSchema && outputFormat !== 'json') {
92
+ console.warn('Warning: --json-schema requires --output-format json, ignoring schema');
93
+ jsonSchema = null;
29
94
  }
95
+ return jsonSchema;
96
+ }
30
97
 
31
- // Add JSON schema if provided (only works with --output-format json)
32
- if (options.jsonSchema) {
33
- if (outputFormat !== 'json') {
34
- console.warn('Warning: --json-schema requires --output-format json, ignoring schema');
35
- } else {
36
- // CRITICAL: Must stringify schema object before passing to CLI (like zeroshot does)
37
- const schemaString =
38
- typeof options.jsonSchema === 'string'
39
- ? options.jsonSchema
40
- : JSON.stringify(options.jsonSchema);
41
- args.push('--json-schema', schemaString);
42
- }
98
+ function resolveModelSpec(options, provider, providerSettings, levelOverrides) {
99
+ if (options.model) {
100
+ return {
101
+ model: options.model,
102
+ reasoningEffort: options.reasoningEffort,
103
+ };
43
104
  }
44
105
 
45
- if (options.resume) {
46
- args.push('--resume', options.resume);
47
- } else if (options.continue) {
48
- args.push('--continue');
106
+ const level = options.modelLevel || providerSettings.defaultLevel || provider.getDefaultLevel();
107
+ let modelSpec = provider.resolveModelSpec(level, levelOverrides);
108
+ if (options.reasoningEffort) {
109
+ modelSpec = { ...modelSpec, reasoningEffort: options.reasoningEffort };
49
110
  }
111
+ return modelSpec;
112
+ }
50
113
 
51
- args.push(prompt);
114
+ function resolveFinalArgs(commandSpec, providerName, options) {
115
+ const finalArgs = [...commandSpec.args];
116
+ if (providerName === 'claude') {
117
+ const promptIndex = finalArgs.length - 1;
118
+ if (options.resume) {
119
+ finalArgs.splice(promptIndex, 0, '--resume', options.resume);
120
+ } else if (options.continue) {
121
+ finalArgs.splice(promptIndex, 0, '--continue');
122
+ }
123
+ } else if (options.resume || options.continue) {
124
+ console.warn('Warning: resume/continue is only supported for Claude CLI; ignoring.');
125
+ }
126
+ return finalArgs;
127
+ }
52
128
 
53
- const task = {
129
+ function buildTaskRecord({ id, prompt, cwd, options, logFile, providerName, modelSpec }) {
130
+ return {
54
131
  id,
55
132
  prompt: prompt.slice(0, 200) + (prompt.length > 200 ? '...' : ''),
56
133
  fullPrompt: prompt,
@@ -63,33 +140,36 @@ export function spawnTask(prompt, options = {}) {
63
140
  updatedAt: new Date().toISOString(),
64
141
  exitCode: null,
65
142
  error: null,
143
+ provider: providerName,
144
+ model: modelSpec?.model || null,
66
145
  // Schedule reference (if spawned by scheduler)
67
146
  scheduleId: options.scheduleId || null,
68
147
  // Attach support
69
148
  socketPath: null,
70
149
  attachable: false,
71
150
  };
151
+ }
72
152
 
73
- addTask(task);
74
-
75
- // Fork a watcher process that will manage the claude process
76
- const watcherConfig = {
153
+ function buildWatcherConfig(outputFormat, jsonSchema, options, providerName, commandSpec) {
154
+ return {
77
155
  outputFormat,
78
- jsonSchema: options.jsonSchema || null,
156
+ jsonSchema,
79
157
  silentJsonOutput: options.silentJsonOutput || false,
80
- model,
158
+ provider: providerName,
159
+ command: commandSpec.binary,
160
+ env: commandSpec.env || {},
81
161
  };
162
+ }
82
163
 
83
- // Use attachable watcher by default (unless explicitly disabled)
84
- // Attachable watcher uses node-pty and creates a Unix socket for attach/detach
85
- const useAttachable = options.attachable !== false;
86
- const watcherScript = useAttachable
87
- ? join(__dirname, 'attachable-watcher.js')
88
- : join(__dirname, 'watcher.js');
164
+ function resolveWatcherScript(options) {
165
+ const useAttachable = options.attachable !== false && !options.jsonSchema;
166
+ return useAttachable ? join(__dirname, 'attachable-watcher.js') : join(__dirname, 'watcher.js');
167
+ }
89
168
 
169
+ function spawnWatcher({ watcherScript, id, cwd, logFile, finalArgs, watcherConfig }) {
90
170
  const watcher = fork(
91
171
  watcherScript,
92
- [id, cwd, logFile, JSON.stringify(args), JSON.stringify(watcherConfig)],
172
+ [id, cwd, logFile, JSON.stringify(finalArgs), JSON.stringify(watcherConfig)],
93
173
  {
94
174
  detached: true,
95
175
  stdio: 'ignore',
@@ -97,9 +177,7 @@ export function spawnTask(prompt, options = {}) {
97
177
  );
98
178
 
99
179
  watcher.unref();
100
-
101
- // Return task immediately - watcher will update PID async
102
- return task;
180
+ watcher.disconnect(); // Close IPC channel so parent can exit
103
181
  }
104
182
 
105
183
  export function isProcessRunning(pid) {
@@ -17,7 +17,7 @@ const CHECK_INTERVAL = 60000; // 60 seconds
17
17
  * Supports: 30s, 5m, 2h, 1d, 1w
18
18
  */
19
19
  export function parseInterval(str) {
20
- const match = str.match(/^(\d+)(s|m|h|d|w)$/i);
20
+ const match = str.match(/^(\d+)([smhdw])$/i);
21
21
  if (!match) return null;
22
22
 
23
23
  const value = parseInt(match[1], 10);
@@ -108,7 +108,7 @@ function log(msg) {
108
108
  /**
109
109
  * Check and run due schedules
110
110
  */
111
- function checkSchedules() {
111
+ async function checkSchedules() {
112
112
  const schedules = loadSchedules();
113
113
  const now = new Date();
114
114
 
@@ -122,7 +122,7 @@ function checkSchedules() {
122
122
  log(`Running scheduled task: ${schedule.id} - "${schedule.prompt.slice(0, 50)}..."`);
123
123
 
124
124
  try {
125
- const task = spawnTask(schedule.prompt, {
125
+ const task = await spawnTask(schedule.prompt, {
126
126
  cwd: schedule.cwd,
127
127
  scheduleId: schedule.id,
128
128
  });