@crewx/cli 0.9.0-rc.128 → 0.9.0-rc.129

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/dist/builtin.d.ts CHANGED
@@ -12,4 +12,4 @@ export declare const BUILTIN_COMMANDS: Set<string>;
12
12
  * Falls back to direct main() call on any tracer failure.
13
13
  */
14
14
  export declare function wrapWithTracer(builtinName: string, main: (args?: string[]) => Promise<void>, args: string[]): Promise<void>;
15
- export declare function handleBuiltin(builtinName: string, args: string[]): Promise<void>;
15
+ export declare function handleBuiltin(builtinName: string, args: string[], productPackageRoot?: string): Promise<void>;
package/dist/builtin.js CHANGED
@@ -106,7 +106,7 @@ async function wrapWithTracer(builtinName, main, args) {
106
106
  }
107
107
  await main(args);
108
108
  }
109
- async function handleBuiltin(builtinName, args) {
109
+ async function handleBuiltin(builtinName, args, productPackageRoot) {
110
110
  const loader = BUILTIN_MAP[builtinName];
111
111
  if (!loader) {
112
112
  console.error(`Unknown built-in tool: ${builtinName}`);
@@ -114,7 +114,10 @@ async function handleBuiltin(builtinName, args) {
114
114
  }
115
115
  try {
116
116
  const mod = await loader();
117
- await wrapWithTracer(builtinName, mod.main.bind(mod), args);
117
+ const main = builtinName === 'skill'
118
+ ? (skillArgs) => mod.main(skillArgs, { productPackageRoot })
119
+ : mod.main.bind(mod);
120
+ await wrapWithTracer(builtinName, main, args);
118
121
  }
119
122
  catch (err) {
120
123
  const code = err.code;
@@ -48,6 +48,12 @@ export declare function resolveReceiptMode(options: {
48
48
  detachedRunner: boolean;
49
49
  agentContext: boolean;
50
50
  }): ReceiptModeDecision;
51
+ /**
52
+ * Rebuild runner argv from the parsed request. The resolved prompt is passed
53
+ * as one argv value after `--`, so stdin is never needed by the detached
54
+ * child and prompt text beginning with a dash remains positional text.
55
+ */
56
+ export declare function buildDetachedRunnerArgs(filteredArgs: string[], agentRef: string, finalMessage: string): string[];
51
57
  export type AssignedTaskAdoption = {
52
58
  kind: 'adopted';
53
59
  taskId: string;
@@ -86,4 +92,4 @@ export declare function runDetachedMiddle(command: string, hop: DetachedMiddleHo
86
92
  * Persist a receipt and launch its detached runner. Unix uses one hop. Windows
87
93
  * uses a short-lived middle hop so the final runner has no live parent link.
88
94
  */
89
- export declare function runDetached(command: string, filteredArgs: string[], reason: ReceiptModeReason): Promise<void>;
95
+ export declare function runDetached(command: string, filteredArgs: string[], reason: ReceiptModeReason, invocationArgs?: string[]): Promise<void>;
@@ -12,6 +12,7 @@ exports.resolveReceiptActor = resolveReceiptActor;
12
12
  exports.reportAdmissionBlocked = reportAdmissionBlocked;
13
13
  exports.extractReceiptFlags = extractReceiptFlags;
14
14
  exports.resolveReceiptMode = resolveReceiptMode;
15
+ exports.buildDetachedRunnerArgs = buildDetachedRunnerArgs;
15
16
  exports.adoptAssignedTask = adoptAssignedTask;
16
17
  exports.readAssignedTaskActor = readAssignedTaskActor;
17
18
  exports.readDetachedMiddleHop = readDetachedMiddleHop;
@@ -26,10 +27,12 @@ const node_1 = require("@crewx/sdk/node");
26
27
  const repository_1 = require("@crewx/sdk/repository");
27
28
  const parse_agent_message_1 = require("./parse-agent-message");
28
29
  const parse_common_flags_1 = require("./parse-common-flags");
30
+ const resolve_recipient_1 = require("./resolve-recipient");
29
31
  const resolve_prompt_1 = require("./resolve-prompt");
30
32
  const version_1 = require("../utils/version");
31
33
  const inherited_trace_1 = require("../utils/inherited-trace");
32
34
  const cal_1 = require("./cal");
35
+ const emit_trailer_1 = require("./emit-trailer");
33
36
  function nonEmptyString(value) {
34
37
  return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
35
38
  }
@@ -73,6 +76,7 @@ function reportAdmissionBlocked(admission) {
73
76
  const DETACHED_RUNNER_POLL_INTERVAL_MS = 200;
74
77
  const DETACHED_RUNNER_ADOPTION_TIMEOUT_MS = 30_000;
75
78
  const DETACHED_VALUE_FLAGS = [
79
+ '--to',
76
80
  '--thread',
77
81
  '--provider',
78
82
  '--model',
@@ -268,6 +272,13 @@ function buildDetachedRunnerArgs(filteredArgs, agentRef, finalMessage) {
268
272
  forwarded.push(arg);
269
273
  continue;
270
274
  }
275
+ if (arg === '--to') {
276
+ // The child receives the resolved recipient as a positional @agent.
277
+ i += 1;
278
+ continue;
279
+ }
280
+ if (arg.startsWith('--to='))
281
+ continue;
271
282
  if (!isDetachedValueFlag(arg))
272
283
  continue;
273
284
  if (isDetachedPromptFileFlag(arg)) {
@@ -426,15 +437,32 @@ async function runDetachedMiddle(command, hop) {
426
437
  * Persist a receipt and launch its detached runner. Unix uses one hop. Windows
427
438
  * uses a short-lived middle hop so the final runner has no live parent link.
428
439
  */
429
- async function runDetached(command, filteredArgs, reason) {
440
+ async function runDetached(command, filteredArgs, reason, invocationArgs = filteredArgs) {
430
441
  const parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(filteredArgs);
431
- const { thread, metadata, promptFile, rest } = parsedFlags;
442
+ const { thread, metadata, promptFile, rest, to } = parsedFlags;
432
443
  const { agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest);
433
444
  const finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
434
- const agentRef = parsedAgentRef || '@crewx';
445
+ let agentRef;
446
+ try {
447
+ ({ agentRef } = (0, resolve_recipient_1.resolveRecipient)({
448
+ positionalRef: parsedAgentRef,
449
+ toFlag: to,
450
+ agentContext: Boolean(process.env['CREWX_AGENT_ID']),
451
+ bodyForHint: finalMessage,
452
+ command,
453
+ invocationArgs,
454
+ }));
455
+ }
456
+ catch (error) {
457
+ const messageText = errorMessage(error);
458
+ process.stderr.write(`Error: ${messageText}\n`);
459
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: (0, inherited_trace_1.readInheritedTrace)(), isError: true });
460
+ process.exit(2);
461
+ return;
462
+ }
435
463
  if (!finalMessage) {
436
- console.error('Usage: crewx query/execute [@agent] <message> [options]');
437
- console.error(' @agent is optional. Defaults to @crewx when omitted.');
464
+ console.error('Usage: crewx q|x --to <agent> <message> [options]');
465
+ console.error(' @agent is a positional shorthand for selecting a recipient.');
438
466
  console.error(' Task body can also be supplied via stdin pipe or --prompt-file.');
439
467
  process.exit(1);
440
468
  return;
@@ -3,6 +3,7 @@
3
3
  * Parses args, loads config via Crewx SDK, and executes the task.
4
4
  *
5
5
  * Flags:
6
+ * --to <agent> Explicit recipient (the positional @agent form is shorthand)
6
7
  * --thread <name> Conversation thread name
7
8
  * --provider <cli/xxx> Provider override
8
9
  * --model <name> Model override (e.g. claude-sonnet-5)
@@ -4,6 +4,7 @@
4
4
  * Parses args, loads config via Crewx SDK, and executes the task.
5
5
  *
6
6
  * Flags:
7
+ * --to <agent> Explicit recipient (the positional @agent form is shorthand)
7
8
  * --thread <name> Conversation thread name
8
9
  * --provider <cli/xxx> Provider override
9
10
  * --model <name> Model override (e.g. claude-sonnet-5)
@@ -27,6 +28,7 @@ const sdk_1 = require("@crewx/sdk");
27
28
  const repository_1 = require("@crewx/sdk/repository");
28
29
  const parse_agent_message_1 = require("./parse-agent-message");
29
30
  const parse_common_flags_1 = require("./parse-common-flags");
31
+ const resolve_recipient_1 = require("./resolve-recipient");
30
32
  const resolve_prompt_1 = require("./resolve-prompt");
31
33
  const crewx_cli_1 = require("../bootstrap/crewx-cli");
32
34
  const inherited_trace_1 = require("../utils/inherited-trace");
@@ -59,7 +61,7 @@ async function handleExecute(args, command = 'execute', emitState = (0, emit_tra
59
61
  agentContext: Boolean(process.env['CREWX_AGENT_ID']),
60
62
  });
61
63
  if (receiptMode.receipt) {
62
- await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason);
64
+ await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason, args);
63
65
  return;
64
66
  }
65
67
  let parsedFlags;
@@ -70,7 +72,7 @@ async function handleExecute(args, command = 'execute', emitState = (0, emit_tra
70
72
  (0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
71
73
  throw err;
72
74
  }
73
- const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = parsedFlags;
75
+ const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest, to } = parsedFlags;
74
76
  const inheritedTrace = (0, inherited_trace_1.readInheritedTrace)();
75
77
  let parsedAgentRef;
76
78
  let message;
@@ -84,20 +86,40 @@ async function handleExecute(args, command = 'execute', emitState = (0, emit_tra
84
86
  (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
85
87
  throw err;
86
88
  }
87
- const agentRef = parsedAgentRef || '@crewx';
89
+ let agentRef;
90
+ try {
91
+ ({ agentRef } = (0, resolve_recipient_1.resolveRecipient)({
92
+ positionalRef: parsedAgentRef,
93
+ toFlag: to,
94
+ agentContext: Boolean(process.env['CREWX_AGENT_ID']),
95
+ bodyForHint: finalMessage,
96
+ command,
97
+ invocationArgs: args,
98
+ }));
99
+ }
100
+ catch (err) {
101
+ const message = err instanceof Error ? err.message : String(err);
102
+ process.stderr.write(`Error: ${message}\n`);
103
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
104
+ process.exit(2);
105
+ return;
106
+ }
88
107
  if (!finalMessage) {
89
- console.error('Usage: crewx execute [@agent] <task> [options]');
90
- console.error(' crewx x [@agent] <task> [options]');
108
+ console.error('Usage: crewx execute --to <agent> <task> [options]');
109
+ console.error(' crewx x --to <agent> <task> [options]');
110
+ console.error(' crewx x @agent <task> [options] (@agent shorthand)');
91
111
  console.error('');
92
- console.error(' @agent is optional. Defaults to @crewx when omitted.');
112
+ console.error(' Agent calls require --to or a positional @agent recipient.');
113
+ console.error(' Human terminal and cron calls default to @crewx when omitted.');
93
114
  console.error(' Task body can also be supplied via stdin pipe or --prompt-file.');
94
115
  console.error('');
95
116
  console.error('Examples:');
96
- console.error(' crewx x "@agent do something"');
97
- console.error(' cat task.md | crewx x "@agent label"');
98
- console.error(' crewx x "@agent label" --prompt-file=task.md');
117
+ console.error(' crewx x --to agent do something');
118
+ console.error(' cat task.md | crewx x --to agent');
119
+ console.error(' crewx x @agent label --prompt-file=task.md');
99
120
  console.error('');
100
121
  console.error('Options:');
122
+ console.error(' --to <agent> Explicit agent recipient');
101
123
  console.error(' --thread <name> Conversation thread name');
102
124
  console.error(' --provider <cli/xxx> Provider override');
103
125
  console.error(' --model <name> Model override (e.g. claude-sonnet-5)');
@@ -2,7 +2,7 @@
2
2
  * Common flag parser for query/execute commands.
3
3
  *
4
4
  * Supports both `--flag=value` and `--flag value` forms.
5
- * Handles: --thread, --provider, --model, --metadata, --verbose, --config/-c,
5
+ * Handles: --to, --thread, --provider, --model, --metadata, --verbose, --config/-c,
6
6
  * --output-format, --effort, --prompt-file/-f, --overdrive.
7
7
  *
8
8
  * Strict mode: unknown --xxx tokens after known flags are consumed throw an
@@ -12,6 +12,8 @@
12
12
  export interface CommonFlags {
13
13
  /** Thread name for conversation continuity. */
14
14
  thread?: string;
15
+ /** Explicit agent recipient. */
16
+ to?: string;
15
17
  /** Provider override (e.g., cli/claude). */
16
18
  provider?: string;
17
19
  /** Model override (e.g., claude-sonnet-5). */
@@ -3,7 +3,7 @@
3
3
  * Common flag parser for query/execute commands.
4
4
  *
5
5
  * Supports both `--flag=value` and `--flag value` forms.
6
- * Handles: --thread, --provider, --model, --metadata, --verbose, --config/-c,
6
+ * Handles: --to, --thread, --provider, --model, --metadata, --verbose, --config/-c,
7
7
  * --output-format, --effort, --prompt-file/-f, --overdrive.
8
8
  *
9
9
  * Strict mode: unknown --xxx tokens after known flags are consumed throw an
@@ -61,6 +61,7 @@ exports.UnknownOptionError = UnknownOptionError;
61
61
  * The `--` sentinel itself is NOT included in `rest`.
62
62
  */
63
63
  function parseCommonFlags(args) {
64
+ const { value: to, consumed: toConsumed } = parseToFlag(args);
64
65
  const thread = parseFlag(args, '--thread');
65
66
  const provider = parseFlag(args, '--provider');
66
67
  const model = parseFlag(args, '--model');
@@ -73,7 +74,7 @@ function parseCommonFlags(args) {
73
74
  const verbose = hasFlag(args, '--verbose');
74
75
  const overdrive = hasFlag(args, '--overdrive');
75
76
  // Collect consumed positions for known flags
76
- const consumed = new Set();
77
+ const consumed = new Set(toConsumed);
77
78
  const flagPairs = [
78
79
  { names: ['--thread'] },
79
80
  { names: ['--provider'] },
@@ -149,7 +150,7 @@ function parseCommonFlags(args) {
149
150
  if (!escapeMode && token.startsWith('--')) {
150
151
  throw new UnknownOptionError(`Unknown option: ${token}. Use "--" to pass literal flag-like tokens as message.\n` +
151
152
  ` Did you mean: crewx serve ${token}\n` +
152
- ` or: crewx q -- ${token} <message> (to include in message)`);
153
+ ` or: crewx q --to <agent> -- ${token} <message>`);
153
154
  }
154
155
  // Reject short -x flags that are not numeric (e.g. -f was consumed above)
155
156
  if (!escapeMode && token.startsWith('-') && token.length > 1 && !/^-?\d/.test(token)) {
@@ -157,7 +158,35 @@ function parseCommonFlags(args) {
157
158
  }
158
159
  rest.push(token);
159
160
  }
160
- return { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest };
161
+ return { to, thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest };
162
+ }
163
+ /** Parse the explicit recipient flag without treating tokens after `--` as flags. */
164
+ function parseToFlag(args) {
165
+ let value;
166
+ const consumed = [];
167
+ for (let index = 0; index < args.length; index += 1) {
168
+ const arg = args[index];
169
+ if (arg === '--')
170
+ break;
171
+ if (arg.startsWith('--to=')) {
172
+ const candidate = arg.slice('--to='.length);
173
+ if (!candidate.trim())
174
+ throw new UnknownOptionError('--to requires an agent id');
175
+ value = candidate;
176
+ consumed.push(index);
177
+ continue;
178
+ }
179
+ if (arg !== '--to')
180
+ continue;
181
+ const candidate = args[index + 1];
182
+ if (candidate === undefined || candidate === '--' || candidate.startsWith('--') || !candidate.trim()) {
183
+ throw new UnknownOptionError('--to requires an agent id');
184
+ }
185
+ value = candidate;
186
+ consumed.push(index, index + 1);
187
+ index += 1;
188
+ }
189
+ return { value, consumed };
161
190
  }
162
191
  /**
163
192
  * Parse metadata JSON string from --metadata flag.
@@ -3,6 +3,7 @@
3
3
  * Parses args, loads config via Crewx SDK, and executes the query.
4
4
  *
5
5
  * Flags:
6
+ * --to <agent> Explicit recipient (the positional @agent form is shorthand)
6
7
  * --thread <name> Conversation thread name
7
8
  * --provider <cli/xxx> Provider override
8
9
  * --model <name> Model override (e.g. claude-sonnet-5)
@@ -18,7 +19,7 @@
18
19
  *
19
20
  * Stdin support:
20
21
  * Pipe or redirect content into crewx q to supply the prompt body via stdin.
21
- * e.g. cat brief.md | crewx q "@agent label"
22
+ * e.g. cat brief.md | crewx q --to agent label
22
23
  * Stdin is ignored when running in an interactive TTY.
23
24
  */
24
25
  import { type DelegationEmitState } from './emit-trailer';
@@ -4,6 +4,7 @@
4
4
  * Parses args, loads config via Crewx SDK, and executes the query.
5
5
  *
6
6
  * Flags:
7
+ * --to <agent> Explicit recipient (the positional @agent form is shorthand)
7
8
  * --thread <name> Conversation thread name
8
9
  * --provider <cli/xxx> Provider override
9
10
  * --model <name> Model override (e.g. claude-sonnet-5)
@@ -19,7 +20,7 @@
19
20
  *
20
21
  * Stdin support:
21
22
  * Pipe or redirect content into crewx q to supply the prompt body via stdin.
22
- * e.g. cat brief.md | crewx q "@agent label"
23
+ * e.g. cat brief.md | crewx q --to agent label
23
24
  * Stdin is ignored when running in an interactive TTY.
24
25
  */
25
26
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -28,6 +29,7 @@ const sdk_1 = require("@crewx/sdk");
28
29
  const repository_1 = require("@crewx/sdk/repository");
29
30
  const parse_agent_message_1 = require("./parse-agent-message");
30
31
  const parse_common_flags_1 = require("./parse-common-flags");
32
+ const resolve_recipient_1 = require("./resolve-recipient");
31
33
  const resolve_prompt_1 = require("./resolve-prompt");
32
34
  const crewx_cli_1 = require("../bootstrap/crewx-cli");
33
35
  const inherited_trace_1 = require("../utils/inherited-trace");
@@ -56,7 +58,7 @@ async function handleQuery(args, command = 'query', emitState = (0, emit_trailer
56
58
  agentContext: Boolean(process.env['CREWX_AGENT_ID']),
57
59
  });
58
60
  if (receiptMode.receipt) {
59
- await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason);
61
+ await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason, args);
60
62
  return;
61
63
  }
62
64
  let parsedFlags;
@@ -67,7 +69,7 @@ async function handleQuery(args, command = 'query', emitState = (0, emit_trailer
67
69
  (0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
68
70
  throw err;
69
71
  }
70
- const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = parsedFlags;
72
+ const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest, to } = parsedFlags;
71
73
  const inheritedTrace = (0, inherited_trace_1.readInheritedTrace)();
72
74
  let parsedAgentRef;
73
75
  let message;
@@ -81,20 +83,40 @@ async function handleQuery(args, command = 'query', emitState = (0, emit_trailer
81
83
  (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
82
84
  throw err;
83
85
  }
84
- const agentRef = parsedAgentRef || '@crewx';
86
+ let agentRef;
87
+ try {
88
+ ({ agentRef } = (0, resolve_recipient_1.resolveRecipient)({
89
+ positionalRef: parsedAgentRef,
90
+ toFlag: to,
91
+ agentContext: Boolean(process.env['CREWX_AGENT_ID']),
92
+ bodyForHint: finalMessage,
93
+ command,
94
+ invocationArgs: args,
95
+ }));
96
+ }
97
+ catch (err) {
98
+ const message = err instanceof Error ? err.message : String(err);
99
+ process.stderr.write(`Error: ${message}\n`);
100
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
101
+ process.exit(2);
102
+ return;
103
+ }
85
104
  if (!finalMessage) {
86
- console.error('Usage: crewx query [@agent] <message> [options]');
87
- console.error(' crewx q [@agent] <message> [options]');
105
+ console.error('Usage: crewx query --to <agent> <message> [options]');
106
+ console.error(' crewx q --to <agent> <message> [options]');
107
+ console.error(' crewx q @agent <message> [options] (@agent shorthand)');
88
108
  console.error('');
89
- console.error(' @agent is optional. Defaults to @crewx when omitted.');
109
+ console.error(' Agent calls require --to or a positional @agent recipient.');
110
+ console.error(' Human terminal and cron calls default to @crewx when omitted.');
90
111
  console.error(' Prompt can also be supplied via stdin pipe or --prompt-file.');
91
112
  console.error('');
92
113
  console.error('Examples:');
93
- console.error(' crewx q "@agent hello"');
94
- console.error(' cat brief.md | crewx q "@agent summarise"');
95
- console.error(' crewx q "@agent label" --prompt-file=brief.md');
114
+ console.error(' crewx q --to agent hello');
115
+ console.error(' cat brief.md | crewx q --to agent');
116
+ console.error(' crewx q @agent label --prompt-file=brief.md');
96
117
  console.error('');
97
118
  console.error('Options:');
119
+ console.error(' --to <agent> Explicit agent recipient');
98
120
  console.error(' --thread <name> Conversation thread name');
99
121
  console.error(' --provider <cli/xxx> Provider override');
100
122
  console.error(' --model <name> Model override (e.g. claude-sonnet-5)');
@@ -14,6 +14,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.readStdin = readStdin;
15
15
  exports.resolvePrompt = resolvePrompt;
16
16
  const fs_1 = require("fs");
17
+ function decodePromptFile(promptFile) {
18
+ const contents = (0, fs_1.readFileSync)(promptFile);
19
+ if (contents.length >= 3
20
+ && contents[0] === 0xef
21
+ && contents[1] === 0xbb
22
+ && contents[2] === 0xbf) {
23
+ return contents.subarray(3).toString('utf8');
24
+ }
25
+ if (contents.length >= 2 && contents[0] === 0xff && contents[1] === 0xfe) {
26
+ return contents.subarray(2).toString('utf16le');
27
+ }
28
+ if (contents.length >= 2 && contents[0] === 0xfe && contents[1] === 0xff) {
29
+ const payload = contents.subarray(2);
30
+ for (let index = 0; index + 1 < payload.length; index += 2) {
31
+ const first = payload[index];
32
+ payload[index] = payload[index + 1];
33
+ payload[index + 1] = first;
34
+ }
35
+ return payload.toString('utf16le');
36
+ }
37
+ return contents.toString('utf8');
38
+ }
17
39
  /**
18
40
  * Read all bytes from stdin (pipe or redirect).
19
41
  * Returns empty string when stdin is a TTY (interactive terminal).
@@ -43,7 +65,7 @@ async function readStdin() {
43
65
  async function resolvePrompt(argvPrompt, promptFile, _readStdin = readStdin) {
44
66
  let external = '';
45
67
  if (promptFile) {
46
- external = (0, fs_1.readFileSync)(promptFile, 'utf8').trimEnd();
68
+ external = decodePromptFile(promptFile).trimEnd();
47
69
  }
48
70
  else if (!argvPrompt) {
49
71
  external = (await _readStdin()).trimEnd();
@@ -0,0 +1,14 @@
1
+ export interface ResolveRecipientOptions {
2
+ positionalRef?: string;
3
+ toFlag?: string;
4
+ agentContext: boolean;
5
+ bodyForHint?: string;
6
+ command?: string;
7
+ invocationArgs?: string[];
8
+ }
9
+ export declare class RecipientResolutionError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ export declare function resolveRecipient(options: ResolveRecipientOptions): {
13
+ agentRef: string;
14
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RecipientResolutionError = void 0;
4
+ exports.resolveRecipient = resolveRecipient;
5
+ class RecipientResolutionError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = 'RecipientResolutionError';
9
+ }
10
+ }
11
+ exports.RecipientResolutionError = RecipientResolutionError;
12
+ function resolveRecipient(options) {
13
+ const toRef = normalizeRecipient(options.toFlag, true);
14
+ const positionalRef = normalizeRecipient(options.positionalRef, false);
15
+ if (toRef && positionalRef && toRef !== positionalRef) {
16
+ throw new RecipientResolutionError(`Recipient was specified twice: --to ${toRef.slice(1)} conflicts with positional ${positionalRef}.`);
17
+ }
18
+ const agentRef = toRef ?? positionalRef;
19
+ if (agentRef)
20
+ return { agentRef };
21
+ if (!options.agentContext)
22
+ return { agentRef: '@crewx' };
23
+ const hintedId = options.bodyForHint?.match(/^@(\S+)/u)?.[1];
24
+ const retryId = hintedId ?? '<agent-id>';
25
+ const command = options.command ?? 'query';
26
+ const retryArgs = (options.invocationArgs ?? []).map(formatRetryArgument).join(' ');
27
+ const retryCommand = `crewx ${command} --to ${retryId}${retryArgs ? ` ${retryArgs}` : ''}`;
28
+ throw new RecipientResolutionError('No recipient was specified. Agent calls must set one explicitly; nothing was run.\n' +
29
+ `Retry command: ${retryCommand}\n` +
30
+ 'To ask the built-in assistant on purpose, use --to crewx.');
31
+ }
32
+ function normalizeRecipient(value, required) {
33
+ if (value === undefined)
34
+ return undefined;
35
+ const id = value.trim().replace(/^@/u, '');
36
+ if (!id) {
37
+ if (required)
38
+ throw new RecipientResolutionError('--to requires an agent id.');
39
+ return undefined;
40
+ }
41
+ return `@${id}`;
42
+ }
43
+ function formatRetryArgument(value) {
44
+ if (!/\s/u.test(value))
45
+ return value;
46
+ return `"${value.replaceAll('"', '\\"')}"`;
47
+ }
package/dist/main.js CHANGED
@@ -86,6 +86,7 @@ const db_1 = require("./commands/db");
86
86
  const parse_common_flags_1 = require("./commands/parse-common-flags");
87
87
  const registry_1 = require("./commands/registry");
88
88
  const version_1 = require("./utils/version");
89
+ const product_package_root_1 = require("./utils/product-package-root");
89
90
  const emit_trailer_1 = require("./commands/emit-trailer");
90
91
  /**
91
92
  * Dev-only SSOT assertion: bin/cli-commands.js must be the exact union of
@@ -123,7 +124,10 @@ async function main(emitState) {
123
124
  }
124
125
  // Built-in tools: memory, search, doc, wbs, cron, workflow, skill
125
126
  if (builtin_1.BUILTIN_COMMANDS.has(command)) {
126
- await (0, builtin_1.handleBuiltin)(command, args.slice(1));
127
+ const productPackageRoot = command === 'skill'
128
+ ? (0, product_package_root_1.resolveProductRootFromCliEntry)(process.argv[1])
129
+ : undefined;
130
+ await (0, builtin_1.handleBuiltin)(command, args.slice(1), productPackageRoot);
127
131
  return;
128
132
  }
129
133
  // Core commands
@@ -276,12 +280,14 @@ UI Modes:
276
280
  crewx electron [--overlay] Launch Electron (desktop or overlay window)
277
281
 
278
282
  Query / Execute:
279
- q|query "@agent <message>" Query an agent (quoted single string)
280
- x|execute "@agent <task>" Execute a task with an agent (quoted single string)
283
+ q|query --to <agent> <message> Query an agent
284
+ x|execute --to <agent> <task> Execute a task with an agent
281
285
 
282
- @agent is optional — defaults to @crewx when omitted.
286
+ Positional @agent <message> is shorthand for selecting a recipient.
287
+ Agent calls require a recipient; human terminal and cron calls default to @crewx.
283
288
 
284
289
  Common flags:
290
+ --to <agent> Explicit agent recipient
285
291
  --thread <name> Conversation thread
286
292
  --provider <cli/xxx> Provider override
287
293
  --metadata <json> Extra metadata (JSON object, double-quoted). Propagated to events/hooks/tracing.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Finds the installed product root from the CLI entry file that is running.
3
+ * The real CLI path is walked to the first package named `crewx`, which must
4
+ * contain the compiled bundle helper. cwd and environment are deliberately ignored.
5
+ */
6
+ export declare function resolveProductRootFromCliEntry(cliEntryPath: string | undefined): string | undefined;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveProductRootFromCliEntry = resolveProductRootFromCliEntry;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const PRODUCT_BUNDLE_HELPER = (0, node_path_1.join)('dist-server', 'common', 'product-app-bundle.js');
7
+ /**
8
+ * Finds the installed product root from the CLI entry file that is running.
9
+ * The real CLI path is walked to the first package named `crewx`, which must
10
+ * contain the compiled bundle helper. cwd and environment are deliberately ignored.
11
+ */
12
+ function resolveProductRootFromCliEntry(cliEntryPath) {
13
+ if (typeof cliEntryPath !== 'string' || cliEntryPath.trim().length === 0)
14
+ return undefined;
15
+ let realCliEntryPath;
16
+ try {
17
+ realCliEntryPath = (0, node_fs_1.realpathSync)(cliEntryPath);
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ let current = (0, node_path_1.dirname)(realCliEntryPath);
23
+ while (true) {
24
+ try {
25
+ const packageJson = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(current, 'package.json'), 'utf8'));
26
+ if (packageJson.name === 'crewx') {
27
+ return (0, node_fs_1.existsSync)((0, node_path_1.join)(current, PRODUCT_BUNDLE_HELPER)) ? current : undefined;
28
+ }
29
+ }
30
+ catch {
31
+ // Continue to the next ancestor when this directory has no valid package manifest.
32
+ }
33
+ const parent = (0, node_path_1.dirname)(current);
34
+ if (parent === current)
35
+ return undefined;
36
+ current = parent;
37
+ }
38
+ return undefined;
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewx/cli",
3
- "version": "0.9.0-rc.128",
3
+ "version": "0.9.0-rc.129",
4
4
  "license": "UNLICENSED",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -25,20 +25,20 @@
25
25
  "better-sqlite3": "*",
26
26
  "dotenv": "17.2.3",
27
27
  "isomorphic-git": "1.37.1",
28
- "@crewx/sdk": "0.9.0-rc.128",
29
- "@crewx/knowledge-core": "0.1.17-rc.138",
30
- "@crewx/search": "0.1.10-rc.124",
31
- "@crewx/memory": "0.1.23-rc.145",
32
- "@crewx/wbs": "0.1.10-rc.154",
33
- "@crewx/cron": "0.1.10-rc.163",
34
- "@crewx/doc": "0.1.9-rc.121",
35
- "@crewx/wi": "0.1.10-rc.148",
36
- "@crewx/chromex": "0.1.0-rc.161",
37
- "@crewx/notify": "0.1.0-rc.99",
38
- "@crewx/shared": "0.0.6-rc.43",
39
- "@crewx/workflow": "0.3.22-rc.174",
40
- "@crewx/app": "0.1.0-rc.9",
41
- "@crewx/skill": "0.1.20-rc.22"
28
+ "@crewx/search": "0.1.10-rc.125",
29
+ "@crewx/sdk": "0.9.0-rc.129",
30
+ "@crewx/memory": "0.1.23-rc.146",
31
+ "@crewx/doc": "0.1.9-rc.122",
32
+ "@crewx/wbs": "0.1.10-rc.155",
33
+ "@crewx/workflow": "0.3.22-rc.175",
34
+ "@crewx/cron": "0.1.10-rc.164",
35
+ "@crewx/skill": "0.1.20-rc.23",
36
+ "@crewx/notify": "0.1.0-rc.100",
37
+ "@crewx/app": "0.1.0-rc.10",
38
+ "@crewx/knowledge-core": "0.1.17-rc.139",
39
+ "@crewx/wi": "0.1.10-rc.149",
40
+ "@crewx/shared": "0.0.6-rc.44",
41
+ "@crewx/chromex": "0.1.0-rc.162"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/better-sqlite3": "*",