paean 0.10.10 → 0.10.11

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.
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Worker Module Exports
3
+ *
4
+ * The task polling/execution WorkerService and SupervisorAgent have been removed
5
+ * (replaced by loop + gateway heartbeat). This module now only exports the
6
+ * executor framework and configuration used by MCP agent tools.
3
7
  */
4
- export { WorkerService, getWorker, resetWorker } from './service.js';
5
8
  export { type WorkerConfig, type WorkerState, type TaskContext, type TaskResult, type WorkerEvent, type WorkerEventHandler, type WorkerStatus, DEFAULT_WORKER_CONFIG, buildTaskPrompt, type ExecutorType, type ExecutorConfig, type ExecutorOptions, type ExecutorResult, type SupervisorDecision, type VerificationResult, type RecoveryStrategy, type PromptCommandOptions, type PromptResult, type WorkerExecutorConfig, DEFAULT_EXECUTOR_CONFIG, type AvailabilityStatus, type AvailabilityAuthStatus, } from './types.js';
6
- export { SupervisorAgent, getSupervisor, resetSupervisor } from './supervisor.js';
7
9
  export { type AgentExecutor, ExecutorRegistry, getExecutorRegistry, resetExecutorRegistry, } from './executors/index.js';
8
10
  export { InternalExecutor } from './executors/internal.js';
9
11
  export { ClaudeExecutor } from './executors/claude.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,qBAAqB,EACrB,eAAe,EAEf,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,uBAAuB,EAEvB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,GAC9B,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlF,OAAO,EACH,KAAK,aAAa,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,EAEnB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC5B,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,qBAAqB,EACrB,eAAe,EAEf,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,uBAAuB,EAEvB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,GAC9B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACH,KAAK,aAAa,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,EAEnB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC5B,MAAM,aAAa,CAAC"}
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * Worker Module Exports
3
+ *
4
+ * The task polling/execution WorkerService and SupervisorAgent have been removed
5
+ * (replaced by loop + gateway heartbeat). This module now only exports the
6
+ * executor framework and configuration used by MCP agent tools.
3
7
  */
4
- export { WorkerService, getWorker, resetWorker } from './service.js';
5
8
  export { DEFAULT_WORKER_CONFIG, buildTaskPrompt, DEFAULT_EXECUTOR_CONFIG, } from './types.js';
6
- // Supervisor Agent
7
- export { SupervisorAgent, getSupervisor, resetSupervisor } from './supervisor.js';
8
9
  // Executor Framework
9
10
  export { ExecutorRegistry, getExecutorRegistry, resetExecutorRegistry, } from './executors/index.js';
10
11
  // Individual Executors
@@ -17,7 +18,7 @@ export { OpenCodeExecutor } from './executors/opencode.js';
17
18
  export { DroidExecutor } from './executors/droid.js';
18
19
  export { ArticulateExecutor } from './executors/articulate.js';
19
20
  export { PaeanclawExecutor } from './executors/paeanclaw.js';
20
- // Worker Config (Phase 3)
21
+ // Worker Config
21
22
  export { getWorkerConfigPath, loadWorkerConfig, saveWorkerConfig, initWorkerConfigIfNeeded, mergeWorkerConfig, isPathInSandbox, getSandboxPaths, validateSandboxPath,
22
23
  // Executor preference management
23
24
  getExecutorPreference, setExecutorPreference, isExecutorUserDisabled, setExecutorEnabled, getAllExecutorPreferences, } from './config.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAQH,qBAAqB,EACrB,eAAe,EAYf,uBAAuB,GAI1B,MAAM,YAAY,CAAC;AAEpB,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElF,qBAAqB;AACrB,OAAO,EAEH,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,uBAAuB;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,0BAA0B;AAC1B,OAAO,EAGH,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB;AACnB,iCAAiC;AACjC,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC5B,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAQH,qBAAqB,EACrB,eAAe,EAYf,uBAAuB,GAI1B,MAAM,YAAY,CAAC;AAEpB,qBAAqB;AACrB,OAAO,EAEH,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,uBAAuB;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,gBAAgB;AAChB,OAAO,EAGH,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB;AACnB,iCAAiC;AACjC,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC5B,MAAM,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paean",
3
- "version": "0.10.10",
3
+ "version": "0.10.11",
4
4
  "description": "Paean AI CLI - Claude Code-like AI agent with local MCP integration, task management, and autonomous worker mode",
5
5
  "author": "Paean AI <dev@paean.ai>",
6
6
  "license": "MIT",
@@ -1,7 +0,0 @@
1
- /**
2
- * Worker Command
3
- * CLI commands for the Local Autonomous Worker
4
- */
5
- import { Command } from 'commander';
6
- export declare const workerCommand: Command;
7
- //# sourceMappingURL=worker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/commands/worker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2EpC,eAAO,MAAM,aAAa,SAC8C,CAAC"}
@@ -1,594 +0,0 @@
1
- /**
2
- * Worker Command
3
- * CLI commands for the Local Autonomous Worker
4
- */
5
- import { Command } from 'commander';
6
- import chalk from 'chalk';
7
- import { existsSync, statSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
8
- import { join } from 'path';
9
- import { WorkerService, DEFAULT_WORKER_CONFIG, getSupervisor, getExecutorRegistry, InternalExecutor, ClaudeExecutor, GeminiExecutor, CursorExecutor, CodexExecutor, OpenCodeExecutor, DroidExecutor, ArticulateExecutor, PaeanclawExecutor, } from '../worker/index.js';
10
- import { isAuthenticated, getConfigDir } from '../utils/config.js';
11
- import * as output from '../utils/output.js';
12
- import { createTodoItem } from '../api/todo.js';
13
- function getWorkerPidFile() {
14
- return join(getConfigDir(), 'worker.pid');
15
- }
16
- function writeWorkerPid(workspace) {
17
- const info = {
18
- pid: process.pid,
19
- startedAt: new Date().toISOString(),
20
- workspace,
21
- };
22
- try {
23
- writeFileSync(getWorkerPidFile(), JSON.stringify(info, null, 2));
24
- }
25
- catch {
26
- // Best effort
27
- }
28
- }
29
- function readWorkerPid() {
30
- try {
31
- if (!existsSync(getWorkerPidFile()))
32
- return null;
33
- const data = JSON.parse(readFileSync(getWorkerPidFile(), 'utf-8'));
34
- return data;
35
- }
36
- catch {
37
- return null;
38
- }
39
- }
40
- function removeWorkerPid() {
41
- try {
42
- unlinkSync(getWorkerPidFile());
43
- }
44
- catch {
45
- // Ignore
46
- }
47
- }
48
- function isWorkerRunning(pid) {
49
- try {
50
- process.kill(pid, 0);
51
- return true;
52
- }
53
- catch {
54
- return false;
55
- }
56
- }
57
- export const workerCommand = new Command('worker')
58
- .description('Autonomous worker mode for continuous task execution');
59
- // Subcommand: worker start
60
- workerCommand
61
- .command('start')
62
- .description('Start the autonomous worker loop')
63
- .option('-i, --interval <ms>', 'Poll interval in milliseconds', String(DEFAULT_WORKER_CONFIG.pollInterval))
64
- .option('-r, --max-retries <n>', 'Maximum retries per task', String(DEFAULT_WORKER_CONFIG.maxRetries))
65
- .option('-t, --timeout <ms>', 'Task timeout in milliseconds', String(DEFAULT_WORKER_CONFIG.taskTimeout))
66
- .option('--no-verification', 'Disable verification step')
67
- .option('-d, --debug', 'Enable debug logging')
68
- .action(async (options) => {
69
- console.log(chalk.yellow('\n [Deprecated] `paean worker start` will be removed in a future release.'));
70
- console.log(chalk.yellow(' Use `paean --worker` to enable the worker within the interactive CLI.\n'));
71
- requireAuth();
72
- // Validate and clamp config values to prevent NaN and unreasonable ranges
73
- const parsedInterval = parseInt(options.interval, 10);
74
- const parsedRetries = parseInt(options.maxRetries, 10);
75
- const parsedTimeout = parseInt(options.timeout, 10);
76
- const config = {
77
- pollInterval: Math.max(1000, isNaN(parsedInterval) ? DEFAULT_WORKER_CONFIG.pollInterval : parsedInterval),
78
- maxRetries: Math.max(1, Math.min(10, isNaN(parsedRetries) ? DEFAULT_WORKER_CONFIG.maxRetries : parsedRetries)),
79
- taskTimeout: Math.max(10000, isNaN(parsedTimeout) ? DEFAULT_WORKER_CONFIG.taskTimeout : parsedTimeout),
80
- verificationEnabled: options.verification !== false,
81
- debug: options.debug ?? false,
82
- autonomousMode: true,
83
- };
84
- console.log('');
85
- console.log(chalk.bold.cyan(' ╔════════════════════════════════════════════╗'));
86
- console.log(chalk.bold.cyan(' ║') + chalk.bold.white(' PAEAN WORKER ') + chalk.green('●') + chalk.white(' Starting...') + chalk.bold.cyan(' ║'));
87
- console.log(chalk.bold.cyan(' ╚════════════════════════════════════════════╝'));
88
- console.log('');
89
- output.tableRow('Poll Interval', `${config.pollInterval / 1000}s`, 20);
90
- output.tableRow('Max Retries', String(config.maxRetries), 20);
91
- output.tableRow('Task Timeout', `${config.taskTimeout / 60000}min`, 20);
92
- output.tableRow('Verification', config.verificationEnabled ? 'Enabled' : 'Disabled', 20);
93
- console.log('');
94
- const worker = new WorkerService(config);
95
- // Write PID file for status command
96
- writeWorkerPid(config.workspace);
97
- // Track statistics
98
- const startTime = Date.now();
99
- // Event handler for UI updates
100
- worker.onEvent((event) => {
101
- const elapsed = formatDuration(Date.now() - startTime);
102
- switch (event.type) {
103
- case 'started':
104
- console.log(chalk.green('✓') + chalk.dim(' Worker started'));
105
- console.log(chalk.dim(` Press Ctrl+C to stop\n`));
106
- break;
107
- case 'stopped':
108
- console.log(chalk.yellow('●') + chalk.dim(' Worker stopped'));
109
- break;
110
- case 'task_claimed':
111
- console.log(chalk.cyan('▶') + ` Task claimed: ${chalk.white(truncate(event.task.content, 50))}`);
112
- console.log(chalk.dim(` ID: ${event.task.id} | Priority: ${event.task.priority}`));
113
- break;
114
- case 'task_started':
115
- console.log(chalk.blue('◉') + ` Executing (attempt ${event.attempt}/${config.maxRetries})...`);
116
- break;
117
- case 'task_completed':
118
- console.log(chalk.green('✓') + ` Task completed in ${formatDuration(event.duration)}`);
119
- console.log('');
120
- break;
121
- case 'task_failed':
122
- console.log(chalk.red('✗') + ` Task failed: ${chalk.dim(truncate(event.error, 60))}`);
123
- if (event.willRetry) {
124
- console.log(chalk.yellow('↻') + chalk.dim(' Will retry...'));
125
- }
126
- break;
127
- case 'task_verification_failed':
128
- console.log(chalk.yellow('⚠') + ' Verification failed, will retry...');
129
- break;
130
- case 'poll_empty':
131
- // Silent - no tasks available
132
- if (config.debug) {
133
- console.log(chalk.dim(` [${elapsed}] No pending tasks`));
134
- }
135
- break;
136
- case 'error':
137
- console.log(chalk.red('✗') + ` Error: ${event.error}`);
138
- break;
139
- }
140
- });
141
- // Handle graceful shutdown with Codex-style double-press Ctrl+C
142
- let cleaningUp = false;
143
- let lastSigintTime = 0;
144
- const cleanup = async () => {
145
- if (cleaningUp)
146
- return;
147
- cleaningUp = true;
148
- // Remove only our own listeners to prevent re-entry
149
- // (don't use removeAllListeners which would remove Ink/other library handlers)
150
- process.off('SIGINT', handleSigint);
151
- process.off('SIGTERM', cleanup);
152
- console.log('');
153
- console.log(chalk.yellow('Stopping worker...'));
154
- try {
155
- await worker.stop();
156
- }
157
- catch (err) {
158
- if (config.debug) {
159
- console.log(chalk.dim(` Warning: cleanup error: ${err.message}`));
160
- }
161
- }
162
- removeWorkerPid();
163
- const state = worker.getState();
164
- console.log('');
165
- console.log(chalk.bold('Session Summary:'));
166
- output.tableRow('Completed', String(state.completedCount), 15);
167
- output.tableRow('Failed', String(state.failedCount), 15);
168
- output.tableRow('Uptime', formatDuration(Date.now() - startTime), 15);
169
- console.log('');
170
- process.exit(0);
171
- };
172
- const handleSigint = () => {
173
- const now = Date.now();
174
- if (now - lastSigintTime < 1000) {
175
- // Double-press within 1s: force quit immediately
176
- cleanup();
177
- }
178
- else {
179
- lastSigintTime = now;
180
- console.log('');
181
- console.log(chalk.dim(' Press Ctrl+C again to stop the worker'));
182
- }
183
- };
184
- process.on('SIGINT', handleSigint);
185
- process.on('SIGTERM', cleanup);
186
- // Start worker
187
- try {
188
- await worker.start();
189
- // Keep process alive
190
- await new Promise(() => { });
191
- }
192
- catch (error) {
193
- console.log(chalk.red('✗') + ` Failed to start worker: ${error.message}`);
194
- process.exit(1);
195
- }
196
- });
197
- // Subcommand: worker status
198
- workerCommand
199
- .command('status')
200
- .description('Check worker status')
201
- .option('--json', 'Output as JSON')
202
- .action((options) => {
203
- const pidInfo = readWorkerPid();
204
- if (!pidInfo) {
205
- console.log(chalk.dim('No worker is currently running.'));
206
- console.log(chalk.dim('Use `paean worker start` to start a worker.'));
207
- return;
208
- }
209
- const running = isWorkerRunning(pidInfo.pid);
210
- if (!running) {
211
- removeWorkerPid();
212
- console.log(chalk.dim('No worker is currently running (stale PID file cleaned up).'));
213
- console.log(chalk.dim('Use `paean worker start` to start a worker.'));
214
- return;
215
- }
216
- if (options.json) {
217
- console.log(JSON.stringify({ ...pidInfo, running }, null, 2));
218
- return;
219
- }
220
- const uptime = formatDuration(Date.now() - new Date(pidInfo.startedAt).getTime());
221
- console.log(chalk.bold.blue('\nWorker Status\n'));
222
- output.tableRow('Status', chalk.green('Running'), 15);
223
- output.tableRow('PID', String(pidInfo.pid), 15);
224
- output.tableRow('Uptime', uptime, 15);
225
- output.tableRow('Started', new Date(pidInfo.startedAt).toLocaleString(), 15);
226
- if (pidInfo.workspace) {
227
- output.tableRow('Workspace', pidInfo.workspace, 15);
228
- }
229
- console.log('');
230
- });
231
- // Subcommand: worker prompt
232
- workerCommand
233
- .command('prompt')
234
- .description('Execute a natural language task with intelligent routing')
235
- .argument('[prompt...]', 'Natural language task prompt')
236
- .option('-p, --prompt <text>', 'Prompt text (alternative to positional)')
237
- .option('-e, --executor <type>', 'Force specific executor (internal|claude|gemini|cursor|codex|opencode|droid|articulate)')
238
- .option('--auto-approve', 'Skip confirmation prompts')
239
- .option('--dry-run', 'Show plan without execution')
240
- .option('--batch', 'Auto-decompose complex tasks into parallel subtasks')
241
- .option('--chain', 'Auto-decompose into dependent chain steps (outputs feed into next)')
242
- .option('-w, --workspace <path>', 'Working directory')
243
- .option('-v, --verbose', 'Enable verbose logging')
244
- .option('--create-task', 'Create as persistent task instead of immediate execution')
245
- .option('--priority <level>', 'Task priority if creating (high|medium|low)', 'medium')
246
- .action(async (promptArgs, options) => {
247
- requireAuth();
248
- // Combine prompt from various sources
249
- const promptText = options.prompt || promptArgs.join(' ');
250
- if (!promptText.trim()) {
251
- output.error('No prompt provided. Usage: paean worker prompt "Your task"');
252
- process.exit(1);
253
- }
254
- console.log('');
255
- console.log(chalk.bold.cyan(' ╔════════════════════════════════════════════╗'));
256
- console.log(chalk.bold.cyan(' ║') + chalk.bold.white(' PAEAN WORKER ') + chalk.magenta('◆') + chalk.white(' Prompt Mode') + chalk.bold.cyan(' ║'));
257
- console.log(chalk.bold.cyan(' ╚════════════════════════════════════════════╝'));
258
- console.log('');
259
- const debug = options.verbose ?? false;
260
- const cwd = options.workspace || process.cwd();
261
- // Validate workspace path
262
- if (options.workspace) {
263
- if (!existsSync(cwd)) {
264
- output.error(`Workspace path does not exist: ${cwd}`);
265
- process.exit(1);
266
- }
267
- try {
268
- if (!statSync(cwd).isDirectory()) {
269
- output.error(`Workspace path is not a directory: ${cwd}`);
270
- process.exit(1);
271
- }
272
- }
273
- catch {
274
- output.error(`Cannot access workspace path: ${cwd}`);
275
- process.exit(1);
276
- }
277
- }
278
- // Initialize supervisor
279
- const supervisor = getSupervisor({ debug, cloudEnabled: true });
280
- // Show input prompt
281
- console.log(chalk.cyan('▶') + chalk.bold(' Prompt:'));
282
- console.log(chalk.dim(` ${truncate(promptText, 70)}`));
283
- console.log('');
284
- // Grooming phase
285
- console.log(chalk.blue('◉') + ' Analyzing task...');
286
- const startTime = Date.now();
287
- const decision = await supervisor.groomTask(promptText, {
288
- projectPath: cwd,
289
- });
290
- console.log(chalk.green('✓') + ' Task analyzed');
291
- console.log('');
292
- // Display decision
293
- console.log(chalk.bold('📋 Supervisor Decision:'));
294
- output.tableRow('Executor', decision.selectedExecutor, 18);
295
- output.tableRow('Confidence', `${(decision.confidence * 100).toFixed(0)}%`, 18);
296
- output.tableRow('Strategy', decision.verificationStrategy || 'none', 18);
297
- if (decision.tags?.length) {
298
- output.tableRow('Tags', decision.tags.join(', '), 18);
299
- }
300
- console.log('');
301
- if (debug) {
302
- console.log(chalk.dim('Reasoning: ' + decision.reasoning));
303
- console.log('');
304
- }
305
- // Show groomed prompt
306
- console.log(chalk.bold('📝 Groomed Prompt:'));
307
- console.log(chalk.dim('─'.repeat(50)));
308
- const lines = decision.groomedPrompt.split('\n').slice(0, 5);
309
- for (const line of lines) {
310
- console.log(chalk.dim(' ' + truncate(line, 60)));
311
- }
312
- if (decision.groomedPrompt.split('\n').length > 5) {
313
- console.log(chalk.dim(' ... (truncated)'));
314
- }
315
- console.log(chalk.dim('─'.repeat(50)));
316
- console.log('');
317
- // Subtasks if any
318
- if (decision.subtasks?.length) {
319
- console.log(chalk.bold('📂 Subtasks:'));
320
- for (const subtask of decision.subtasks) {
321
- console.log(chalk.dim(` • ${truncate(subtask, 60)}`));
322
- }
323
- console.log('');
324
- }
325
- // Chain mode - dependent sequential tasks
326
- if (options.chain) {
327
- console.log(chalk.blue('◉') + ' Analyzing for chain decomposition...');
328
- const chainResult = await supervisor.decomposeChain(promptText, { projectPath: cwd });
329
- if (chainResult.isChain && chainResult.chain && chainResult.chain.length > 0) {
330
- const chainId = `chain-${Date.now()}`;
331
- console.log(chalk.green('✓') + ` Decomposed into ${chainResult.chain.length} chain steps`);
332
- console.log(chalk.dim(` Reasoning: ${chainResult.reasoning}`));
333
- console.log('');
334
- console.log(chalk.bold('🔗 Creating chain tasks:'));
335
- const createdSteps = [];
336
- // Determine executor: user-specified --executor takes priority over LLM suggestions
337
- const forcedExecutor = options.executor;
338
- if (forcedExecutor) {
339
- console.log(chalk.cyan('ℹ') + ` Using forced executor: ${chalk.bold(forcedExecutor)}`);
340
- }
341
- for (let i = 0; i < chainResult.chain.length; i++) {
342
- const step = chainResult.chain[i];
343
- try {
344
- await createTodoItem({
345
- content: step.content,
346
- priority: step.priority || options.priority,
347
- metadata: {
348
- chainId,
349
- stepId: step.stepId,
350
- stepIndex: i + 1,
351
- totalSteps: chainResult.chain.length,
352
- dependsOn: step.dependsOn,
353
- expectedOutputs: step.expectedOutputs,
354
- inputFrom: step.inputFrom,
355
- suggestedExecutor: forcedExecutor || step.suggestedExecutor,
356
- workingDirectory: cwd,
357
- },
358
- });
359
- createdSteps.push(step.stepId);
360
- const deps = step.dependsOn?.length ? ` (depends: ${step.dependsOn.join(', ')})` : '';
361
- console.log(chalk.green(' ✓') + ` [${step.stepId}] ${truncate(step.content, 45)}${deps}`);
362
- }
363
- catch (err) {
364
- console.log(chalk.red(' ✗') + ` Failed: ${err.message}`);
365
- }
366
- }
367
- console.log('');
368
- console.log(chalk.green('✓') + ` Created chain: ${chainId}`);
369
- if (chainResult.estimatedDurationMinutes) {
370
- console.log(chalk.dim(` Estimated duration: ~${chainResult.estimatedDurationMinutes} minutes`));
371
- }
372
- console.log(chalk.dim(` Run 'paean worker start' to process this chain.`));
373
- return;
374
- }
375
- else {
376
- console.log(chalk.yellow('ℹ') + ' Task is not a chain, proceeding with single execution');
377
- console.log('');
378
- }
379
- }
380
- // Batch mode - decompose and queue
381
- if (options.batch) {
382
- console.log(chalk.blue('◉') + ' Analyzing for batch decomposition...');
383
- const decomposition = await supervisor.decomposeTask(promptText, { projectPath: cwd });
384
- if (decomposition.shouldDecompose && decomposition.subtasks && decomposition.subtasks.length > 1) {
385
- console.log(chalk.green('✓') + ` Decomposed into ${decomposition.subtasks.length} subtasks`);
386
- console.log(chalk.dim(` Reasoning: ${decomposition.reasoning}`));
387
- console.log('');
388
- console.log(chalk.bold('📋 Creating batch tasks:'));
389
- const createdIds = [];
390
- // Determine executor: user-specified --executor takes priority over LLM suggestions
391
- const forcedBatchExecutor = options.executor;
392
- if (forcedBatchExecutor) {
393
- console.log(chalk.cyan('ℹ') + ` Using forced executor: ${chalk.bold(forcedBatchExecutor)}`);
394
- }
395
- for (let i = 0; i < decomposition.subtasks.length; i++) {
396
- const subtask = decomposition.subtasks[i];
397
- try {
398
- const result = await createTodoItem({
399
- content: subtask.content,
400
- priority: subtask.priority || options.priority,
401
- metadata: {
402
- parentPrompt: promptText,
403
- subtaskIndex: i + 1,
404
- totalSubtasks: decomposition.subtasks.length,
405
- verificationHint: subtask.verificationHint,
406
- suggestedExecutor: forcedBatchExecutor || subtask.suggestedExecutor,
407
- workingDirectory: cwd,
408
- },
409
- });
410
- createdIds.push(result.data.id);
411
- console.log(chalk.green(' ✓') + ` [${i + 1}/${decomposition.subtasks.length}] ${truncate(subtask.content, 50)}`);
412
- }
413
- catch (err) {
414
- console.log(chalk.red(' ✗') + ` Failed: ${err.message}`);
415
- }
416
- }
417
- console.log('');
418
- console.log(chalk.green('✓') + ` Created ${createdIds.length} tasks`);
419
- if (decomposition.estimatedDurationMinutes) {
420
- console.log(chalk.dim(` Estimated duration: ~${decomposition.estimatedDurationMinutes} minutes`));
421
- }
422
- console.log(chalk.dim(` Run 'paean worker start' to process these tasks.`));
423
- return;
424
- }
425
- else {
426
- console.log(chalk.yellow('ℹ') + ' Task is atomic, proceeding with single execution');
427
- console.log('');
428
- }
429
- }
430
- // Dry run mode - stop here
431
- if (options.dryRun) {
432
- console.log(chalk.yellow('●') + ' Dry run mode - execution skipped');
433
- console.log(chalk.dim(` Duration: ${formatDuration(Date.now() - startTime)}`));
434
- return;
435
- }
436
- // Create as persistent task
437
- if (options.createTask) {
438
- console.log(chalk.blue('◉') + ' Creating persistent task...');
439
- try {
440
- const result = await createTodoItem({
441
- content: promptText,
442
- description: decision.groomedPrompt,
443
- priority: options.priority,
444
- tags: decision.tags,
445
- });
446
- console.log(chalk.green('✓') + ` Task created: ${result.data.id}`);
447
- console.log(chalk.dim(` Use 'paean worker start' to process this task.`));
448
- }
449
- catch (err) {
450
- console.log(chalk.red('✗') + ` Failed to create task: ${err.message}`);
451
- }
452
- return;
453
- }
454
- // Initialize executor registry
455
- const registry = getExecutorRegistry();
456
- // Register all executors
457
- registry.register(new InternalExecutor());
458
- registry.register(new ClaudeExecutor());
459
- registry.register(new GeminiExecutor());
460
- registry.register(new CursorExecutor());
461
- registry.register(new CodexExecutor());
462
- registry.register(new OpenCodeExecutor());
463
- registry.register(new DroidExecutor());
464
- registry.register(new ArticulateExecutor());
465
- registry.register(new PaeanclawExecutor());
466
- // Determine which executor to use
467
- const executorType = options.executor || decision.selectedExecutor;
468
- // Check availability
469
- const available = await registry.getAvailable();
470
- if (debug) {
471
- console.log(chalk.dim(`Available executors: ${available.join(', ')}`));
472
- }
473
- if (!available.includes(executorType) && executorType !== 'internal') {
474
- console.log(chalk.yellow('⚠') + ` Executor '${executorType}' not available, falling back to internal`);
475
- }
476
- const finalExecutor = available.includes(executorType) ? executorType : 'internal';
477
- // Execute
478
- console.log(chalk.blue('◉') + ` Executing with ${chalk.bold(finalExecutor)} executor...`);
479
- console.log(chalk.dim('─'.repeat(50)));
480
- const result = await registry.execute(finalExecutor, decision.groomedPrompt, {
481
- cwd,
482
- skipPermissions: options.autoApprove,
483
- timeout: 600000,
484
- onOutput: (text, stream) => {
485
- // Display streaming output with nice formatting
486
- const lines = text.split('\n');
487
- for (const line of lines) {
488
- if (line.trim()) {
489
- const prefix = stream === 'stderr' ? chalk.yellow('│') : chalk.dim('│');
490
- const displayLine = line.length > 70 ? line.slice(0, 67) + '...' : line;
491
- console.log(prefix + ' ' + displayLine);
492
- }
493
- }
494
- },
495
- });
496
- console.log(chalk.dim('─'.repeat(50)));
497
- const totalDuration = Date.now() - startTime;
498
- // Display result
499
- if (result.success) {
500
- console.log(chalk.green('✓') + ` Execution completed`);
501
- }
502
- else {
503
- console.log(chalk.red('✗') + ` Execution failed`);
504
- if (result.error) {
505
- console.log(chalk.dim(` Error: ${truncate(result.error, 60)}`));
506
- }
507
- }
508
- console.log('');
509
- console.log(chalk.bold('📊 Summary:'));
510
- output.tableRow('Status', result.success ? 'Success' : 'Failed', 15);
511
- output.tableRow('Executor', finalExecutor, 15);
512
- output.tableRow('Duration', formatDuration(totalDuration), 15);
513
- console.log('');
514
- // Show output preview if verbose
515
- if (debug && result.output) {
516
- console.log(chalk.bold('Output Preview:'));
517
- console.log(chalk.dim('─'.repeat(50)));
518
- const outputLines = result.output.split('\n').slice(0, 10);
519
- for (const line of outputLines) {
520
- console.log(chalk.dim(truncate(line, 70)));
521
- }
522
- if (result.output.split('\n').length > 10) {
523
- console.log(chalk.dim('... (truncated)'));
524
- }
525
- console.log(chalk.dim('─'.repeat(50)));
526
- }
527
- process.exit(result.success ? 0 : 1);
528
- });
529
- // Subcommand: worker schedules
530
- workerCommand
531
- .command('schedules')
532
- .description('List and manage scheduled/recurring tasks')
533
- .option('--active', 'Show only active schedules')
534
- .option('--paused', 'Show only paused schedules')
535
- .option('-s, --search <query>', 'Search by task content')
536
- .action(async (options) => {
537
- requireAuth();
538
- try {
539
- const { getScheduledTasks } = await import('../api/todo.js');
540
- const enabled = options.active ? true : options.paused ? false : undefined;
541
- const result = await getScheduledTasks({
542
- enabled,
543
- search: options.search,
544
- });
545
- if (!result.success || !result.data?.items?.length) {
546
- console.log(chalk.yellow('\n No scheduled tasks found.\n'));
547
- console.log(chalk.dim(' Scheduled tasks are created via the Paean agent. Try:'));
548
- console.log(chalk.dim(' "@Paean send a sales overview every weekday at 9am"\n'));
549
- process.exit(0);
550
- }
551
- const items = result.data.items;
552
- console.log('');
553
- console.log(chalk.bold.cyan(` 📅 Scheduled Tasks (${items.length})`));
554
- console.log(chalk.dim(' ' + '─'.repeat(55)));
555
- console.log('');
556
- for (const item of items) {
557
- const schedule = item.schedule;
558
- const enabled = schedule?.enabled;
559
- const statusBadge = enabled
560
- ? chalk.green('● active')
561
- : chalk.yellow('○ paused');
562
- console.log(` ${chalk.bold(item.content)}`);
563
- console.log(` ${chalk.dim('ID:')} ${item.id} ${statusBadge}`);
564
- if (schedule?.cron) {
565
- console.log(` ${chalk.dim('Cron:')} ${schedule.cron} ${chalk.dim('TZ:')} ${schedule.timezone || 'UTC'}`);
566
- }
567
- if (schedule?.nextTriggerAt && enabled) {
568
- console.log(` ${chalk.dim('Next:')} ${schedule.nextTriggerAt}`);
569
- }
570
- if (schedule?.lastTriggeredAt) {
571
- console.log(` ${chalk.dim('Last:')} ${schedule.lastTriggeredAt}`);
572
- }
573
- if ((schedule?.consecutiveFailures || 0) > 0) {
574
- console.log(` ${chalk.red('Failures:')} ${schedule.consecutiveFailures}/${schedule.maxConsecutiveFailures || 3}`);
575
- }
576
- console.log('');
577
- }
578
- }
579
- catch (error) {
580
- output.error(`Failed to fetch scheduled tasks: ${error.message}`);
581
- process.exit(1);
582
- }
583
- });
584
- // Helper functions
585
- function requireAuth() {
586
- if (!isAuthenticated()) {
587
- output.error('Not authenticated. Please run "paean login" first.');
588
- process.exit(1);
589
- }
590
- }
591
- // Use shared utilities from output module
592
- const truncate = output.truncate;
593
- const formatDuration = output.formatDuration;
594
- //# sourceMappingURL=worker.js.map