@crewx/cli 0.9.0-rc.10 → 0.9.0-rc.100

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 (56) hide show
  1. package/dist/bootstrap/codex-writable-roots.d.ts +13 -0
  2. package/dist/bootstrap/codex-writable-roots.js +25 -0
  3. package/dist/bootstrap/crewx-cli.js +7 -2
  4. package/dist/builtin.js +1 -0
  5. package/dist/commands/agent.js +0 -58
  6. package/dist/commands/cal.d.ts +15 -0
  7. package/dist/commands/cal.js +44 -0
  8. package/dist/commands/db.d.ts +2 -0
  9. package/dist/commands/db.js +381 -1
  10. package/dist/commands/detached.d.ts +65 -0
  11. package/dist/commands/detached.js +507 -0
  12. package/dist/commands/doctor.d.ts +53 -0
  13. package/dist/commands/doctor.js +391 -21
  14. package/dist/commands/emit-trailer.d.ts +25 -0
  15. package/dist/commands/emit-trailer.js +33 -0
  16. package/dist/commands/execute.d.ts +7 -4
  17. package/dist/commands/execute.js +118 -17
  18. package/dist/commands/hook/command-marker.d.ts +2 -0
  19. package/dist/commands/hook/command-marker.js +5 -0
  20. package/dist/commands/hook/install.d.ts +0 -1
  21. package/dist/commands/hook/install.js +60 -63
  22. package/dist/commands/hook/status.js +3 -3
  23. package/dist/commands/hook/uninstall.js +2 -2
  24. package/dist/commands/init.js +22 -1
  25. package/dist/commands/kill.d.ts +2 -8
  26. package/dist/commands/kill.js +6 -47
  27. package/dist/commands/log.js +4 -3
  28. package/dist/commands/parse-common-flags.d.ts +5 -1
  29. package/dist/commands/parse-common-flags.js +6 -2
  30. package/dist/commands/ps.d.ts +2 -2
  31. package/dist/commands/ps.js +25 -15
  32. package/dist/commands/publish.d.ts +1 -0
  33. package/dist/commands/publish.js +303 -0
  34. package/dist/commands/query.d.ts +5 -1
  35. package/dist/commands/query.js +110 -11
  36. package/dist/commands/registry.js +4 -2
  37. package/dist/commands/result.d.ts +8 -3
  38. package/dist/commands/result.js +139 -7
  39. package/dist/commands/shortcut.d.ts +1 -0
  40. package/dist/commands/shortcut.js +267 -0
  41. package/dist/commands/slack.js +2 -1
  42. package/dist/commands/stop.d.ts +8 -0
  43. package/dist/commands/stop.js +119 -0
  44. package/dist/commands/write-output.d.ts +3 -0
  45. package/dist/commands/write-output.js +24 -0
  46. package/dist/logging.d.ts +1 -1
  47. package/dist/logging.js +3 -2
  48. package/dist/main.d.ts +3 -2
  49. package/dist/main.js +66 -16
  50. package/dist/utils/env-defaults.d.ts +2 -5
  51. package/dist/utils/env-defaults.js +10 -5
  52. package/dist/utils/inherited-trace.d.ts +24 -0
  53. package/dist/utils/inherited-trace.js +40 -0
  54. package/dist/utils/sdk-compat.d.ts +24 -0
  55. package/dist/utils/sdk-compat.js +120 -0
  56. package/package.json +14 -11
@@ -6,6 +6,7 @@
6
6
  * Flags:
7
7
  * --thread <name> Conversation thread name
8
8
  * --provider <cli/xxx> Provider override
9
+ * --model <name> Model override (e.g. claude-sonnet-5)
9
10
  * --metadata <json> Extra metadata JSON (double-quoted object). Propagated to events/hooks/tracing.
10
11
  * e.g. --metadata='{"workflow_id":"wf-1"}'
11
12
  * --verbose Debug output mode (default: raw agent response only)
@@ -13,6 +14,8 @@
13
14
  * --output-format <fmt> Output format (json|text|stream-json)
14
15
  * --effort <level> Model effort (high|medium|low)
15
16
  * -f/--prompt-file <path> Read prompt body from file (bypasses cmd.exe argv truncation)
17
+ * --detach/--async Persist a receipt and launch a detached runner
18
+ * --wait Force synchronous execution
16
19
  *
17
20
  * Stdin support:
18
21
  * Pipe or redirect content into crewx q to supply the prompt body via stdin.
@@ -22,24 +25,63 @@
22
25
  Object.defineProperty(exports, "__esModule", { value: true });
23
26
  exports.handleQuery = handleQuery;
24
27
  const sdk_1 = require("@crewx/sdk");
28
+ const repository_1 = require("@crewx/sdk/repository");
25
29
  const parse_agent_message_1 = require("./parse-agent-message");
26
30
  const parse_common_flags_1 = require("./parse-common-flags");
27
31
  const resolve_prompt_1 = require("./resolve-prompt");
28
32
  const crewx_cli_1 = require("../bootstrap/crewx-cli");
29
33
  const inherited_trace_1 = require("../utils/inherited-trace");
34
+ const write_output_1 = require("./write-output");
35
+ const detached_1 = require("./detached");
36
+ const emit_trailer_1 = require("./emit-trailer");
30
37
  /**
31
38
  * Handle `crewx query <agentRef> <message>` command.
32
39
  *
33
40
  * Default output: raw agent response only (stdout).
34
41
  * --verbose: debug info written to stderr, response to stdout.
35
42
  */
36
- async function handleQuery(args) {
37
- const { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, rest } = (0, parse_common_flags_1.parseCommonFlags)(args);
38
- const { agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest);
39
- // No @mention → default to @crewx agent (matches cli-bak behaviour)
43
+ async function handleQuery(args, command = 'query', emitState = (0, emit_trailer_1.createDelegationEmitState)()) {
44
+ // The private middle-hop marker is consumed before public flag parsing. An
45
+ // invalid direct use remains an unknown option and is rejected normally.
46
+ const middleHop = (0, detached_1.readDetachedMiddleHop)(args);
47
+ if (middleHop) {
48
+ await (0, detached_1.runDetachedMiddle)(command, middleHop);
49
+ return;
50
+ }
51
+ const receiptFlags = (0, detached_1.extractReceiptFlags)(args);
52
+ const receiptMode = (0, detached_1.resolveReceiptMode)({
53
+ detach: receiptFlags.detach,
54
+ wait: receiptFlags.wait,
55
+ detachedRunner: (0, inherited_trace_1.isDetachedRunner)(),
56
+ agentContext: Boolean(process.env['CREWX_AGENT_ID']),
57
+ });
58
+ if (receiptMode.receipt) {
59
+ await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason);
60
+ return;
61
+ }
62
+ let parsedFlags;
63
+ try {
64
+ parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(receiptFlags.rest);
65
+ }
66
+ catch (err) {
67
+ (0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
68
+ throw err;
69
+ }
70
+ const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = parsedFlags;
71
+ const inheritedTrace = (0, inherited_trace_1.readInheritedTrace)();
72
+ let parsedAgentRef;
73
+ let message;
74
+ let finalMessage;
75
+ try {
76
+ ({ agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest));
77
+ // Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
78
+ finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
79
+ }
80
+ catch (err) {
81
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
82
+ throw err;
83
+ }
40
84
  const agentRef = parsedAgentRef || '@crewx';
41
- // Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
42
- const finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
43
85
  if (!finalMessage) {
44
86
  console.error('Usage: crewx query [@agent] <message> [options]');
45
87
  console.error(' crewx q [@agent] <message> [options]');
@@ -55,6 +97,7 @@ async function handleQuery(args) {
55
97
  console.error('Options:');
56
98
  console.error(' --thread <name> Conversation thread name');
57
99
  console.error(' --provider <cli/xxx> Provider override');
100
+ console.error(' --model <name> Model override (e.g. claude-sonnet-5)');
58
101
  console.error(' --metadata <json> Extra metadata (JSON object, double-quoted).');
59
102
  console.error(' Propagated to events/hooks/tracing.');
60
103
  console.error(' Invalid JSON aborts with exit code 2.');
@@ -62,24 +105,37 @@ async function handleQuery(args) {
62
105
  console.error(' --verbose Debug output mode');
63
106
  console.error(' --config/-c <path> Config file path');
64
107
  console.error(' --output-format <fmt> Output format (json|text|stream-json)');
108
+ console.error(' --out/-o <path> Save result to file (stdout suppressed)');
65
109
  console.error(' --effort <level> Model effort (high|medium|low)');
66
110
  console.error(' -f/--prompt-file <path> Read prompt body from file');
67
111
  console.error(' --var key=value Template variable (repeatable)');
68
112
  console.error(' --overdrive Activate overdrive (boost) profile for this request');
113
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
69
114
  process.exit(1);
115
+ return;
70
116
  }
71
117
  const configPath = config ?? process.env.CREWX_CONFIG ?? 'crewx.yaml';
72
118
  // Only show exec audit span JSON in verbose mode
73
119
  (0, sdk_1.setAuditVerbose)(verbose);
74
- const crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
120
+ let crewx;
121
+ try {
122
+ crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
123
+ }
124
+ catch (err) {
125
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
126
+ throw err;
127
+ }
75
128
  // file:// remote agent delegation is handled transparently inside Crewx.query/execute.
76
129
  if (verbose) {
130
+ process.stderr.write(`mode=sync reason=${receiptMode.reason}\n`);
77
131
  process.stderr.write(`📋 Task: ${finalMessage}\n`);
78
132
  process.stderr.write(`🤖 Agent: ${agentRef}\n`);
79
133
  if (thread)
80
134
  process.stderr.write(`🔗 Thread: ${thread}\n`);
81
135
  if (provider)
82
136
  process.stderr.write(`🔌 Provider: ${provider}\n`);
137
+ if (model)
138
+ process.stderr.write(`🧠 Model: ${model}\n`);
83
139
  if (outputFormat)
84
140
  process.stderr.write(`📄 Output-format: ${outputFormat}\n`);
85
141
  if (effort)
@@ -95,22 +151,43 @@ async function handleQuery(args) {
95
151
  catch (err) {
96
152
  const msg = err instanceof Error ? err.message : String(err);
97
153
  process.stderr.write(`Error: ${msg}\n`);
154
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
98
155
  process.exit(2);
156
+ return;
157
+ }
158
+ // Only the detached runner may adopt a pre-issued task row. Trace variables
159
+ // describe the caller chain and are never used as this task's identity.
160
+ const detachedRunner = (0, inherited_trace_1.isDetachedRunner)();
161
+ const assignedTaskId = (0, inherited_trace_1.readRunnerAssignment)();
162
+ let selfTaskId;
163
+ if (assignedTaskId !== undefined) {
164
+ const adoption = (0, detached_1.adoptAssignedTask)(new repository_1.TaskRepository(), assignedTaskId, detachedRunner);
165
+ if (adoption.kind === 'failed') {
166
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
167
+ process.exit(1);
168
+ return;
169
+ }
170
+ if (adoption.kind === 'adopted')
171
+ selfTaskId = adoption.taskId;
99
172
  }
100
173
  let exitCode = 0;
174
+ let result;
101
175
  try {
102
- const result = await crewx.query(agentRef, finalMessage, {
176
+ result = await crewx.query(agentRef, finalMessage, {
103
177
  provider,
178
+ model,
104
179
  effort: effort || undefined,
105
180
  overdrive: overdrive || undefined,
106
181
  threadId: thread,
182
+ taskId: selfTaskId,
107
183
  metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
108
184
  vars: Object.keys(vars).length > 0 ? vars : undefined,
109
- trace: (0, inherited_trace_1.readInheritedTrace)(),
185
+ trace: inheritedTrace,
110
186
  });
111
187
  if (!result.ok) {
112
188
  const errMsg = result.error?.message ?? 'Query failed';
113
189
  console.error(errMsg);
190
+ (0, write_output_1.appendError)(out, errMsg);
114
191
  exitCode = 1;
115
192
  }
116
193
  else {
@@ -123,7 +200,7 @@ async function handleQuery(args) {
123
200
  process.stderr.write('\n📄 Response:\n');
124
201
  process.stderr.write('─'.repeat(40) + '\n');
125
202
  }
126
- console.log(result.data);
203
+ (0, write_output_1.writeResult)(out, result.data);
127
204
  if (verbose) {
128
205
  process.stderr.write('\n✅ Query completed successfully\n');
129
206
  }
@@ -132,11 +209,33 @@ async function handleQuery(args) {
132
209
  catch (err) {
133
210
  const errMsg = err instanceof Error ? err.message : String(err);
134
211
  console.error(`Error: ${errMsg}`);
212
+ (0, write_output_1.appendError)(out, `Error: ${errMsg}`);
135
213
  exitCode = 1;
136
214
  }
137
215
  finally {
138
- await crewx.close();
216
+ try {
217
+ await crewx.close();
218
+ }
219
+ catch (err) {
220
+ (0, emit_trailer_1.emitDelegationResult)({
221
+ command,
222
+ trace: inheritedTrace,
223
+ taskId: result?.meta.taskId,
224
+ agentId: result?.meta.agentId,
225
+ isError: true,
226
+ state: emitState,
227
+ });
228
+ throw err;
229
+ }
139
230
  }
231
+ (0, emit_trailer_1.emitDelegationResult)({
232
+ command,
233
+ trace: inheritedTrace,
234
+ taskId: result?.meta.taskId,
235
+ agentId: result?.meta.agentId,
236
+ isError: exitCode !== 0 || result?.ok === false,
237
+ state: emitState,
238
+ });
140
239
  if (exitCode !== 0)
141
240
  process.exit(exitCode);
142
241
  }
@@ -13,17 +13,19 @@ exports.KNOWN_COMMANDS = new Set([
13
13
  'q', 'query',
14
14
  'x', 'execute',
15
15
  'agent',
16
- 'ps', 'kill', 'result', 'log', 'restart',
16
+ 'ps', 'stop', 'kill', 'result', 'log', 'restart',
17
17
  'doctor', 'init',
18
18
  'db',
19
19
  'help',
20
+ 'shortcut',
21
+ 'publish',
20
22
  'slack', 'slack:files',
21
23
  'hook', 'hook-dispatch',
22
24
  ]);
23
25
  /** Built-in tool commands routed via handleBuiltin(). */
24
26
  exports.BUILTIN_COMMAND_NAMES = new Set([
25
27
  'memory', 'search', 'doc', 'wbs', 'cron', 'workflow', 'skill', 'dreaming',
26
- 'wi', 'chromex',
28
+ 'wi', 'chromex', 'notify',
27
29
  ]);
28
30
  /** Commands not yet migrated from cli-bak — show a migration message. */
29
31
  exports.NOT_YET_MIGRATED = new Set([
@@ -3,8 +3,13 @@
3
3
  * Retrieves the result of a completed task by its ID.
4
4
  *
5
5
  * Usage:
6
- * crewx result <task-id> Print raw result
7
- * crewx result <task-id> --json Print full task record as JSON
8
- * crewx result List recent tasks (latest 10)
6
+ * crewx result <task-id> Print raw result
7
+ * crewx result <task-id> --json Print full task record as JSON
8
+ * crewx result <task-id> --wait=<seconds> Poll (1s interval) up to the given
9
+ * number of seconds for
10
+ * the task to leave 'pending'/'running'. Exit 124 on
11
+ * timeout. --wait=0 behaves like no --wait
12
+ * (single immediate check).
13
+ * crewx result List recent tasks (latest 10)
9
14
  */
10
15
  export declare function handleResult(args: string[]): Promise<void>;
@@ -4,25 +4,95 @@
4
4
  * Retrieves the result of a completed task by its ID.
5
5
  *
6
6
  * Usage:
7
- * crewx result <task-id> Print raw result
8
- * crewx result <task-id> --json Print full task record as JSON
9
- * crewx result List recent tasks (latest 10)
7
+ * crewx result <task-id> Print raw result
8
+ * crewx result <task-id> --json Print full task record as JSON
9
+ * crewx result <task-id> --wait=<seconds> Poll (1s interval) up to the given
10
+ * number of seconds for
11
+ * the task to leave 'pending'/'running'. Exit 124 on
12
+ * timeout. --wait=0 behaves like no --wait
13
+ * (single immediate check).
14
+ * crewx result List recent tasks (latest 10)
10
15
  */
11
16
  Object.defineProperty(exports, "__esModule", { value: true });
12
17
  exports.handleResult = handleResult;
18
+ const sdk_1 = require("@crewx/sdk");
13
19
  const repository_1 = require("@crewx/sdk/repository");
20
+ const cal_1 = require("./cal");
21
+ const POLL_INTERVAL_MS = 1000;
22
+ const REAP_INTERVAL_MS = 10_000;
14
23
  function statusIcon(status) {
15
24
  switch (status) {
25
+ case 'pending': return '🕓';
16
26
  case 'running': return '⏳';
17
27
  case 'success': return '✅';
18
28
  case 'failed': return '❌';
29
+ case 'stopped': return '⏹';
30
+ case 'runner_lost': return '⚠️';
19
31
  default: return '❓';
20
32
  }
21
33
  }
34
+ function isActiveStatus(status) {
35
+ return status === 'pending' || status === 'running';
36
+ }
37
+ function sleep(ms) {
38
+ return new Promise((resolve) => setTimeout(resolve, ms));
39
+ }
40
+ function resultCommandId(status) {
41
+ switch (status) {
42
+ case 'pending': return 'crewx.result-pending';
43
+ case 'running': return 'crewx.result-running';
44
+ case 'success': return 'crewx.result-done';
45
+ case 'failed':
46
+ case 'stopped':
47
+ case 'runner_lost':
48
+ return 'crewx.result-terminal-abnormal';
49
+ default: return 'crewx.result';
50
+ }
51
+ }
52
+ function writeResultCalFooter(task, lastWaitSeconds) {
53
+ const params = { taskId: task.id };
54
+ if (task.agent_id)
55
+ params.agent = task.agent_id;
56
+ if (task.thread_id)
57
+ params.thread = task.thread_id;
58
+ if (isActiveStatus(task.status)) {
59
+ params.wait = String((0, cal_1.suggestWaitSeconds)({
60
+ startedAt: task.started_at,
61
+ lastWaitSeconds,
62
+ }));
63
+ }
64
+ (0, cal_1.writeCalFooter)({ commandId: resultCommandId(task.status), params });
65
+ }
66
+ function formatRuntimeDuration(startedAt, now = Date.now()) {
67
+ const startedAtMs = Date.parse(startedAt);
68
+ const elapsedSeconds = Number.isFinite(startedAtMs)
69
+ ? Math.max(0, Math.floor((now - startedAtMs) / 1000))
70
+ : 0;
71
+ const hours = Math.floor(elapsedSeconds / 3600);
72
+ const minutes = Math.floor((elapsedSeconds % 3600) / 60);
73
+ const seconds = elapsedSeconds % 60;
74
+ if (hours > 0)
75
+ return `${hours}h ${minutes}m ${seconds}s`;
76
+ if (minutes > 0)
77
+ return `${minutes}m ${seconds}s`;
78
+ return `${seconds}s`;
79
+ }
80
+ /** Parses `--wait=<seconds>`. Returns undefined when the flag is absent. */
81
+ function parseWaitSeconds(args) {
82
+ const arg = args.find(a => a.startsWith('--wait='));
83
+ if (arg === undefined)
84
+ return undefined;
85
+ const n = Number(arg.slice('--wait='.length));
86
+ return Number.isFinite(n) && n >= 0 ? n : 0;
87
+ }
22
88
  async function handleResult(args) {
23
89
  const jsonMode = args.includes('--json');
90
+ const waitSeconds = parseWaitSeconds(args);
24
91
  const taskId = args.find(a => !a.startsWith('--'));
25
92
  const repo = new repository_1.TaskRepository();
93
+ const initialReaped = repo.reapOrphanedTasks({ tickIntervalMs: REAP_INTERVAL_MS });
94
+ if (initialReaped > 0)
95
+ console.error(`Reaped ${initialReaped} orphaned task(s)`);
26
96
  if (!taskId) {
27
97
  const all = repo.getAllTasks();
28
98
  const recent = all.slice(0, 10);
@@ -36,35 +106,97 @@ async function handleResult(args) {
36
106
  const icon = statusIcon(task.status);
37
107
  console.log(`${idx + 1}. ${icon} ${task.id}`);
38
108
  console.log(` Agent: ${task.agent_id ?? '—'} Mode: ${task.mode ?? '—'}`);
39
- console.log(` Started: ${new Date(task.started_at).toLocaleString()}`);
109
+ console.log(` Started: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.started_at))}`);
40
110
  if (task.completed_at) {
41
- console.log(` Completed: ${new Date(task.completed_at).toLocaleString()}`);
111
+ console.log(` Completed: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.completed_at))}`);
42
112
  }
43
113
  console.log('');
44
114
  });
45
115
  console.log('Tip: Run `crewx result <task-id>` to see full output.');
116
+ if (!jsonMode)
117
+ writeResultCalFooter(recent[0]);
46
118
  return;
47
119
  }
48
- const task = repo.getTask(taskId);
120
+ let task = repo.getTask(taskId);
49
121
  if (!task) {
50
122
  console.error(`Error: Task not found: ${taskId}`);
51
123
  process.exit(1);
52
124
  return;
53
125
  }
126
+ if (waitSeconds !== undefined && waitSeconds > 0 && isActiveStatus(task.status)) {
127
+ const deadline = Date.now() + waitSeconds * 1000;
128
+ let lastReapAt = Date.now();
129
+ while (task && isActiveStatus(task.status)) {
130
+ if (Date.now() >= deadline) {
131
+ console.error(`Task ${taskId} is still ${task.status} (waited ${waitSeconds}s; ` +
132
+ `running for ${formatRuntimeDuration(task.started_at)}). ` +
133
+ 'Not stalled — check again with a longer wait.');
134
+ writeResultCalFooter(task, waitSeconds);
135
+ process.exit(124);
136
+ return;
137
+ }
138
+ await sleep(Math.min(POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
139
+ if (Date.now() - lastReapAt >= REAP_INTERVAL_MS) {
140
+ const reaped = repo.reapOrphanedTasks({ tickIntervalMs: REAP_INTERVAL_MS });
141
+ if (reaped > 0)
142
+ console.error(`Reaped ${reaped} orphaned task(s)`);
143
+ lastReapAt = Date.now();
144
+ }
145
+ task = repo.getTask(taskId);
146
+ if (!task) {
147
+ console.error(`Error: Task not found: ${taskId}`);
148
+ process.exit(1);
149
+ return;
150
+ }
151
+ }
152
+ }
54
153
  if (jsonMode) {
55
154
  console.log(JSON.stringify(task, null, 2));
56
155
  return;
57
156
  }
157
+ if (task.status === 'pending') {
158
+ console.error(`🕓 Task ${taskId} is pending (waiting to start — runner has not adopted the task yet).`);
159
+ const nextWaitSeconds = (0, cal_1.suggestWaitSeconds)({
160
+ startedAt: task.started_at,
161
+ lastWaitSeconds: waitSeconds,
162
+ });
163
+ console.error(`Use \`crewx result ${taskId} --wait=${nextWaitSeconds}\` to wait for completion.`);
164
+ writeResultCalFooter(task, waitSeconds);
165
+ process.exit(1);
166
+ return;
167
+ }
58
168
  if (task.status === 'running') {
59
169
  console.error(`Task ${taskId} is still running.`);
60
- console.error(`Use \`crewx kill ${taskId}\` to stop it.`);
170
+ const nextWaitSeconds = (0, cal_1.suggestWaitSeconds)({
171
+ startedAt: task.started_at,
172
+ lastWaitSeconds: waitSeconds,
173
+ });
174
+ console.error(`Use \`crewx result ${taskId} --wait=${nextWaitSeconds}\` to wait longer or \`crewx log ${taskId}\` to inspect progress.`);
175
+ writeResultCalFooter(task, waitSeconds);
176
+ process.exit(1);
177
+ return;
178
+ }
179
+ if (task.status === 'stopped') {
180
+ if (task.result !== null && task.result !== undefined) {
181
+ console.log(task.result);
182
+ }
183
+ console.error(`⏹ Task ${taskId} was stopped by user: ${task.error ?? 'no further details'}.`);
184
+ writeResultCalFooter(task);
185
+ process.exit(1);
186
+ return;
187
+ }
188
+ if (task.status === 'runner_lost') {
189
+ console.error(`⚠️ Task ${taskId} runner lost before completion: ${task.error ?? 'no further details'}.`);
190
+ writeResultCalFooter(task);
61
191
  process.exit(1);
62
192
  return;
63
193
  }
64
194
  if (task.status === 'failed') {
65
195
  console.error(`Task ${taskId} failed: ${task.error ?? 'unknown error'}`);
196
+ writeResultCalFooter(task);
66
197
  process.exit(1);
67
198
  return;
68
199
  }
69
200
  console.log(task.result ?? '');
201
+ writeResultCalFooter(task);
70
202
  }
@@ -0,0 +1 @@
1
+ export declare function handleShortcut(args: string[]): Promise<void>;