@crewx/cli 0.9.0-rc.14 → 0.9.0-rc.141

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 (65) 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 +27 -6
  4. package/dist/builtin.d.ts +1 -1
  5. package/dist/builtin.js +7 -2
  6. package/dist/commands/agent.js +1 -107
  7. package/dist/commands/cal.d.ts +15 -0
  8. package/dist/commands/cal.js +44 -0
  9. package/dist/commands/db.d.ts +3 -0
  10. package/dist/commands/db.js +533 -1
  11. package/dist/commands/detached.d.ts +95 -0
  12. package/dist/commands/detached.js +620 -0
  13. package/dist/commands/doctor.d.ts +77 -0
  14. package/dist/commands/doctor.js +475 -22
  15. package/dist/commands/emit-trailer.d.ts +25 -0
  16. package/dist/commands/emit-trailer.js +33 -0
  17. package/dist/commands/execute.d.ts +8 -4
  18. package/dist/commands/execute.js +200 -51
  19. package/dist/commands/hook/command-marker.d.ts +2 -0
  20. package/dist/commands/hook/command-marker.js +5 -0
  21. package/dist/commands/hook/install.d.ts +0 -1
  22. package/dist/commands/hook/install.js +75 -61
  23. package/dist/commands/hook/status.js +3 -3
  24. package/dist/commands/hook/uninstall.js +2 -2
  25. package/dist/commands/init.d.ts +34 -0
  26. package/dist/commands/init.js +132 -59
  27. package/dist/commands/kill.d.ts +2 -8
  28. package/dist/commands/kill.js +6 -47
  29. package/dist/commands/log.js +4 -3
  30. package/dist/commands/parse-common-flags.d.ts +5 -1
  31. package/dist/commands/parse-common-flags.js +35 -4
  32. package/dist/commands/ps.d.ts +2 -2
  33. package/dist/commands/ps.js +25 -15
  34. package/dist/commands/publish.d.ts +1 -0
  35. package/dist/commands/publish.js +303 -0
  36. package/dist/commands/query.d.ts +7 -2
  37. package/dist/commands/query.js +195 -48
  38. package/dist/commands/registry.js +4 -2
  39. package/dist/commands/resolve-prompt.js +23 -1
  40. package/dist/commands/resolve-recipient.d.ts +14 -0
  41. package/dist/commands/resolve-recipient.js +47 -0
  42. package/dist/commands/restart.d.ts +3 -1
  43. package/dist/commands/restart.js +14 -4
  44. package/dist/commands/result.d.ts +8 -3
  45. package/dist/commands/result.js +149 -10
  46. package/dist/commands/shortcut.d.ts +1 -0
  47. package/dist/commands/shortcut.js +267 -0
  48. package/dist/commands/slack.js +2 -1
  49. package/dist/commands/stop.d.ts +8 -0
  50. package/dist/commands/stop.js +119 -0
  51. package/dist/index.d.ts +1 -1
  52. package/dist/index.js +4 -1
  53. package/dist/logging.d.ts +1 -1
  54. package/dist/logging.js +3 -2
  55. package/dist/main.d.ts +3 -2
  56. package/dist/main.js +99 -24
  57. package/dist/utils/env-defaults.d.ts +2 -5
  58. package/dist/utils/env-defaults.js +10 -5
  59. package/dist/utils/inherited-trace.d.ts +24 -0
  60. package/dist/utils/inherited-trace.js +40 -0
  61. package/dist/utils/product-package-root.d.ts +6 -0
  62. package/dist/utils/product-package-root.js +39 -0
  63. package/dist/utils/sdk-compat.d.ts +24 -0
  64. package/dist/utils/sdk-compat.js +120 -0
  65. package/package.json +16 -12
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.handlePublish = handlePublish;
37
+ /**
38
+ * crewx publish handler — thin CLI wrapper over @crewx/sdk/publish.
39
+ *
40
+ * Usage:
41
+ * crewx publish [dir] [--dry-run] [--version <semver>] [--json] [--upload]
42
+ *
43
+ * All scanning, exclusion-rule judgment, secret detection, hashing, and
44
+ * tar.gz packing lives in @crewx/sdk/publish (planPublish / packTemplate).
45
+ * Submit+upload (WI-SHR-20260806-013) lives in the same package
46
+ * (uploadToMarketplace) for the same reason — see that module's header.
47
+ * This command only parses argv, forwards options, asks the SDK SessionManager
48
+ * for a valid account bearer when upload is requested, and renders the result.
49
+ */
50
+ const fs = __importStar(require("fs"));
51
+ const account_1 = require("@crewx/sdk/account");
52
+ const publish_1 = require("@crewx/sdk/publish");
53
+ const parse_common_flags_1 = require("./parse-common-flags");
54
+ function parsePublishFlags(args) {
55
+ const flags = { dryRun: false, json: false, help: false, upload: false };
56
+ for (let i = 0; i < args.length; i++) {
57
+ const arg = args[i];
58
+ if (arg === '--help' || arg === '-h') {
59
+ flags.help = true;
60
+ continue;
61
+ }
62
+ if (arg === '--dry-run') {
63
+ flags.dryRun = true;
64
+ continue;
65
+ }
66
+ if (arg === '--json') {
67
+ flags.json = true;
68
+ continue;
69
+ }
70
+ if (arg === '--upload') {
71
+ flags.upload = true;
72
+ continue;
73
+ }
74
+ if (arg === '--version') {
75
+ const value = args[i + 1];
76
+ if (value === undefined)
77
+ throw new parse_common_flags_1.UnknownOptionError('--version requires a value');
78
+ flags.version = value;
79
+ i++;
80
+ continue;
81
+ }
82
+ if (arg.startsWith('--version=')) {
83
+ flags.version = arg.slice('--version='.length);
84
+ continue;
85
+ }
86
+ if (arg.startsWith('-')) {
87
+ throw new parse_common_flags_1.UnknownOptionError(`Unknown option: ${arg}`);
88
+ }
89
+ if (flags.dir === undefined) {
90
+ flags.dir = arg;
91
+ continue;
92
+ }
93
+ throw new parse_common_flags_1.UnknownOptionError(`Unknown option: ${arg}`);
94
+ }
95
+ return flags;
96
+ }
97
+ function resolveWorkspaceDir(flags) {
98
+ return flags.dir ?? process.env['CREWX_WORKSPACE'] ?? process.cwd();
99
+ }
100
+ function printJson(payload) {
101
+ console.log(JSON.stringify(payload));
102
+ }
103
+ /** Prints the error and exits 1. Never returns (matches `process.exit`'s `never` type). */
104
+ function failWithError(err, json) {
105
+ const message = err instanceof Error ? err.message : String(err);
106
+ if (json) {
107
+ printJson({ success: false, error: message });
108
+ }
109
+ else {
110
+ console.error(`āœ— ${message}`);
111
+ }
112
+ process.exit(1);
113
+ }
114
+ function printHelp() {
115
+ console.log(`
116
+ crewx publish — package a workspace as a distributable template archive
117
+
118
+ Usage:
119
+ crewx publish [dir] [--dry-run] [--version <semver>] [--json] [--upload]
120
+
121
+ Arguments:
122
+ dir Workspace to publish (default: $CREWX_WORKSPACE or cwd)
123
+
124
+ Options:
125
+ --dry-run Scan + build manifest only; do not write a .tgz
126
+ --version <ver> Override manifest version (semver, e.g. 1.0.0)
127
+ --json Print machine-readable JSON to stdout
128
+ --upload Submit + upload the packed archive to marketplace
129
+ (uses the configured account target and a prior login;
130
+ ignored when combined with --dry-run)
131
+ --help, -h Show this help
132
+
133
+ Notes:
134
+ Without --upload, this command only scans the workspace, applies
135
+ exclusion rules, checks for secrets, and packs a local
136
+ .crewx/publish/<name>-<version>.tgz archive.
137
+ `.trim());
138
+ }
139
+ /** Resolve and validate the Marketplace target immediately before an upload. */
140
+ function requireMarketplaceUrl() {
141
+ try {
142
+ const url = (0, account_1.resolveMarketplaceUrl)();
143
+ (0, account_1.assertAccountTargetNotMixed)();
144
+ return url;
145
+ }
146
+ catch (error) {
147
+ const code = typeof error === 'object' && error !== null && 'code' in error
148
+ ? error.code
149
+ : undefined;
150
+ if (code === 'ACCOUNT_TARGET_INVALID' ||
151
+ code === 'ACCOUNT_TARGET_MIXED' ||
152
+ code === 'MARKETPLACE_DISABLED' ||
153
+ code === 'MARKETPLACE_URL_INVALID') {
154
+ const message = error instanceof Error ? error.message : String(error);
155
+ throw new Error(`publish: [${code}] ${message}`);
156
+ }
157
+ throw error;
158
+ }
159
+ }
160
+ function isMarketplaceUnauthorized(err) {
161
+ return err instanceof Error
162
+ && err.message.includes('publish: [E_AUTH_EXPIRED]')
163
+ && /HTTP 401\b/.test(err.message);
164
+ }
165
+ async function uploadWithSessionRefresh(sessionManager, options, accessToken) {
166
+ try {
167
+ return await (0, publish_1.uploadToMarketplace)({ ...options, accessToken });
168
+ }
169
+ catch (err) {
170
+ if (!isMarketplaceUnauthorized(err))
171
+ throw err;
172
+ const refreshedSession = await sessionManager.refresh();
173
+ return (0, publish_1.uploadToMarketplace)({ ...options, accessToken: refreshedSession.access_token });
174
+ }
175
+ }
176
+ async function handlePublish(args) {
177
+ const flags = parsePublishFlags(args);
178
+ if (flags.help) {
179
+ printHelp();
180
+ return;
181
+ }
182
+ const dir = resolveWorkspaceDir(flags);
183
+ process.stderr.write(`[publish] scanning ${dir}...\n`);
184
+ let plan;
185
+ try {
186
+ plan = await (0, publish_1.planPublish)(dir, { version: flags.version });
187
+ }
188
+ catch (err) {
189
+ failWithError(err, flags.json);
190
+ }
191
+ if (plan.secretFindings.length > 0) {
192
+ if (flags.json) {
193
+ printJson({ success: false, error: 'secret findings detected', findings: plan.secretFindings });
194
+ }
195
+ else {
196
+ console.error('āœ— secret findings detected — publish aborted');
197
+ for (const f of plan.secretFindings) {
198
+ console.error(` ${f.path}:${f.line} (${f.rule})`);
199
+ }
200
+ }
201
+ process.exit(1);
202
+ }
203
+ if (flags.dryRun) {
204
+ if (flags.json) {
205
+ printJson({
206
+ success: true,
207
+ dryRun: true,
208
+ workspace: dir,
209
+ manifest: plan.manifest,
210
+ included: plan.included,
211
+ excluded: plan.excluded,
212
+ });
213
+ }
214
+ else {
215
+ console.log('āœ“ dry-run — no archive written');
216
+ console.log(` workspace: ${dir}`);
217
+ console.log(` name: ${plan.manifest.name} version: ${plan.manifest.version}`);
218
+ console.log(` included: ${plan.included.length} files`);
219
+ console.log(` excluded: ${plan.excluded.length} entries`);
220
+ for (const e of plan.excluded) {
221
+ console.log(` ${e.path} (${e.rule})`);
222
+ }
223
+ }
224
+ return;
225
+ }
226
+ // Resolved before packing (not after) so a doomed --upload run (no
227
+ // marketplace url / no session) never writes a stray .tgz to the
228
+ // workspace — WI-SHR-20260806-013 AC-U5.
229
+ let marketplaceUrl = '';
230
+ let accessToken = '';
231
+ let sessionManager;
232
+ if (flags.upload) {
233
+ try {
234
+ marketplaceUrl = requireMarketplaceUrl();
235
+ sessionManager = new account_1.SessionManager({ authClient: new account_1.AuthClient() });
236
+ accessToken = await sessionManager.getAccessToken();
237
+ }
238
+ catch (err) {
239
+ if (err instanceof Error && !err.message.startsWith('publish: [')) {
240
+ failWithError(new Error(`publish: [E_AUTH_EXPIRED] Authentication is missing or expired — please log in again — ${err.message}`), flags.json);
241
+ }
242
+ failWithError(err, flags.json);
243
+ }
244
+ }
245
+ process.stderr.write(`[publish] packing ${plan.manifest.name}@${plan.manifest.version}...\n`);
246
+ let packed;
247
+ try {
248
+ packed = await (0, publish_1.packTemplate)(dir, { version: flags.version });
249
+ }
250
+ catch (err) {
251
+ failWithError(err, flags.json);
252
+ }
253
+ const fileCount = packed.manifest.files.length;
254
+ const totalBytes = fs.statSync(packed.tgzPath).size;
255
+ let uploadResult;
256
+ if (flags.upload) {
257
+ process.stderr.write(`[publish] uploading ${packed.manifest.name}@${packed.manifest.version} to ${marketplaceUrl}...\n`);
258
+ try {
259
+ if (!sessionManager)
260
+ throw new Error('publish: [E_AUTH_EXPIRED] Account session is unavailable');
261
+ uploadResult = await uploadWithSessionRefresh(sessionManager, {
262
+ tgzPath: packed.tgzPath,
263
+ manifest: packed.manifest,
264
+ baseUrl: marketplaceUrl,
265
+ }, accessToken);
266
+ }
267
+ catch (err) {
268
+ failWithError(err, flags.json);
269
+ }
270
+ process.stderr.write(`[publish] submit -> ${uploadResult.submitOutcome}\n`);
271
+ }
272
+ if (flags.json) {
273
+ printJson({
274
+ success: true,
275
+ tgzPath: packed.tgzPath,
276
+ fileCount,
277
+ totalBytes,
278
+ manifest: packed.manifest,
279
+ ...(uploadResult
280
+ ? {
281
+ slug: uploadResult.slug,
282
+ version: uploadResult.version,
283
+ artifactChecksum: uploadResult.artifactChecksum,
284
+ artifactSizeBytes: uploadResult.artifactSizeBytes,
285
+ submitOutcome: uploadResult.submitOutcome,
286
+ versionUpdate: uploadResult.versionUpdate,
287
+ serverVersionBefore: uploadResult.serverVersionBefore,
288
+ }
289
+ : {}),
290
+ });
291
+ }
292
+ else {
293
+ console.log(`āœ“ packed: ${packed.tgzPath}`);
294
+ console.log(` files: ${fileCount} bytes: ${totalBytes}`);
295
+ if (uploadResult) {
296
+ console.log(`āœ“ uploaded: ${uploadResult.slug}@${uploadResult.version} (${uploadResult.submitOutcome})`);
297
+ console.log(` artifactChecksum: ${uploadResult.artifactChecksum} artifactSizeBytes: ${uploadResult.artifactSizeBytes}`);
298
+ }
299
+ }
300
+ if (!flags.upload) {
301
+ console.error('⚠ To upload, use --upload — check the generated file path');
302
+ }
303
+ }
@@ -3,8 +3,10 @@
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
9
+ * --model <name> Model override (e.g. claude-sonnet-5)
8
10
  * --metadata <json> Extra metadata JSON (double-quoted object). Propagated to events/hooks/tracing.
9
11
  * e.g. --metadata='{"workflow_id":"wf-1"}'
10
12
  * --verbose Debug output mode (default: raw agent response only)
@@ -12,16 +14,19 @@
12
14
  * --output-format <fmt> Output format (json|text|stream-json)
13
15
  * --effort <level> Model effort (high|medium|low)
14
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
15
19
  *
16
20
  * Stdin support:
17
21
  * Pipe or redirect content into crewx q to supply the prompt body via stdin.
18
- * e.g. cat brief.md | crewx q "@agent label"
22
+ * e.g. cat brief.md | crewx q --to agent label
19
23
  * Stdin is ignored when running in an interactive TTY.
20
24
  */
25
+ import { type DelegationEmitState } from './emit-trailer';
21
26
  /**
22
27
  * Handle `crewx query <agentRef> <message>` command.
23
28
  *
24
29
  * Default output: raw agent response only (stdout).
25
30
  * --verbose: debug info written to stderr, response to stdout.
26
31
  */
27
- export declare function handleQuery(args: string[]): Promise<void>;
32
+ export declare function handleQuery(args: string[], command?: string, emitState?: DelegationEmitState): Promise<void>;
@@ -4,8 +4,10 @@
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
10
+ * --model <name> Model override (e.g. claude-sonnet-5)
9
11
  * --metadata <json> Extra metadata JSON (double-quoted object). Propagated to events/hooks/tracing.
10
12
  * e.g. --metadata='{"workflow_id":"wf-1"}'
11
13
  * --verbose Debug output mode (default: raw agent response only)
@@ -13,49 +15,111 @@
13
15
  * --output-format <fmt> Output format (json|text|stream-json)
14
16
  * --effort <level> Model effort (high|medium|low)
15
17
  * -f/--prompt-file <path> Read prompt body from file (bypasses cmd.exe argv truncation)
18
+ * --detach/--async Persist a receipt and launch a detached runner
19
+ * --wait Force synchronous execution
16
20
  *
17
21
  * Stdin support:
18
22
  * Pipe or redirect content into crewx q to supply the prompt body via stdin.
19
- * e.g. cat brief.md | crewx q "@agent label"
23
+ * e.g. cat brief.md | crewx q --to agent label
20
24
  * Stdin is ignored when running in an interactive TTY.
21
25
  */
22
26
  Object.defineProperty(exports, "__esModule", { value: true });
23
27
  exports.handleQuery = handleQuery;
24
28
  const sdk_1 = require("@crewx/sdk");
29
+ const repository_1 = require("@crewx/sdk/repository");
25
30
  const parse_agent_message_1 = require("./parse-agent-message");
26
31
  const parse_common_flags_1 = require("./parse-common-flags");
32
+ const resolve_recipient_1 = require("./resolve-recipient");
27
33
  const resolve_prompt_1 = require("./resolve-prompt");
28
34
  const crewx_cli_1 = require("../bootstrap/crewx-cli");
29
35
  const inherited_trace_1 = require("../utils/inherited-trace");
30
36
  const write_output_1 = require("./write-output");
37
+ const detached_1 = require("./detached");
38
+ const emit_trailer_1 = require("./emit-trailer");
31
39
  /**
32
40
  * Handle `crewx query <agentRef> <message>` command.
33
41
  *
34
42
  * Default output: raw agent response only (stdout).
35
43
  * --verbose: debug info written to stderr, response to stdout.
36
44
  */
37
- async function handleQuery(args) {
38
- const { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = (0, parse_common_flags_1.parseCommonFlags)(args);
39
- const { agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest);
40
- // No @mention → default to @crewx agent (matches cli-bak behaviour)
41
- const agentRef = parsedAgentRef || '@crewx';
42
- // Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
43
- const finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
45
+ async function handleQuery(args, command = 'query', emitState = (0, emit_trailer_1.createDelegationEmitState)()) {
46
+ // The private middle-hop marker is consumed before public flag parsing. An
47
+ // invalid direct use remains an unknown option and is rejected normally.
48
+ const middleHop = (0, detached_1.readDetachedMiddleHop)(args);
49
+ if (middleHop) {
50
+ await (0, detached_1.runDetachedMiddle)(command, middleHop);
51
+ return;
52
+ }
53
+ const receiptFlags = (0, detached_1.extractReceiptFlags)(args);
54
+ const receiptMode = (0, detached_1.resolveReceiptMode)({
55
+ detach: receiptFlags.detach,
56
+ wait: receiptFlags.wait,
57
+ detachedRunner: (0, inherited_trace_1.isDetachedRunner)(),
58
+ agentContext: Boolean(process.env['CREWX_AGENT_ID']),
59
+ });
60
+ if (receiptMode.receipt) {
61
+ await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason, args);
62
+ return;
63
+ }
64
+ let parsedFlags;
65
+ try {
66
+ parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(receiptFlags.rest);
67
+ }
68
+ catch (err) {
69
+ (0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
70
+ throw err;
71
+ }
72
+ const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest, to } = parsedFlags;
73
+ const inheritedTrace = (0, inherited_trace_1.readInheritedTrace)();
74
+ let parsedAgentRef;
75
+ let message;
76
+ let finalMessage;
77
+ try {
78
+ ({ agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest));
79
+ // Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
80
+ finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
81
+ }
82
+ catch (err) {
83
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
84
+ throw err;
85
+ }
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
+ }
44
104
  if (!finalMessage) {
45
- console.error('Usage: crewx query [@agent] <message> [options]');
46
- 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(' Human-terminal shorthand: crewx q "@agent <message>"');
47
108
  console.error('');
48
- 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.');
49
111
  console.error(' Prompt can also be supplied via stdin pipe or --prompt-file.');
50
112
  console.error('');
51
113
  console.error('Examples:');
52
- console.error(' crewx q "@agent hello"');
53
- console.error(' cat brief.md | crewx q "@agent summarise"');
54
- console.error(' crewx q "@agent label" --prompt-file=brief.md');
114
+ console.error(' crewx q --to agent "hello"');
115
+ console.error(' crewx q --to agent -f brief.md');
116
+ console.error(' Prompt files contain only the body; set the recipient separately with --to.');
55
117
  console.error('');
56
118
  console.error('Options:');
119
+ console.error(' --to <agent> Explicit agent recipient');
57
120
  console.error(' --thread <name> Conversation thread name');
58
121
  console.error(' --provider <cli/xxx> Provider override');
122
+ console.error(' --model <name> Model override (e.g. claude-sonnet-5)');
59
123
  console.error(' --metadata <json> Extra metadata (JSON object, double-quoted).');
60
124
  console.error(' Propagated to events/hooks/tracing.');
61
125
  console.error(' Invalid JSON aborts with exit code 2.');
@@ -63,24 +127,37 @@ async function handleQuery(args) {
63
127
  console.error(' --verbose Debug output mode');
64
128
  console.error(' --config/-c <path> Config file path');
65
129
  console.error(' --output-format <fmt> Output format (json|text|stream-json)');
130
+ console.error(' --out/-o <path> Save result to file (stdout suppressed)');
66
131
  console.error(' --effort <level> Model effort (high|medium|low)');
67
- console.error(' -f/--prompt-file <path> Read prompt body from file');
132
+ console.error(' -f/--prompt-file <path> Read body only from file; set recipient separately with --to');
68
133
  console.error(' --var key=value Template variable (repeatable)');
69
134
  console.error(' --overdrive Activate overdrive (boost) profile for this request');
135
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
70
136
  process.exit(1);
137
+ return;
71
138
  }
72
139
  const configPath = config ?? process.env.CREWX_CONFIG ?? 'crewx.yaml';
73
140
  // Only show exec audit span JSON in verbose mode
74
141
  (0, sdk_1.setAuditVerbose)(verbose);
75
- const crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
142
+ let crewx;
143
+ try {
144
+ crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
145
+ }
146
+ catch (err) {
147
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
148
+ throw err;
149
+ }
76
150
  // file:// remote agent delegation is handled transparently inside Crewx.query/execute.
77
151
  if (verbose) {
152
+ process.stderr.write(`mode=sync reason=${receiptMode.reason}\n`);
78
153
  process.stderr.write(`šŸ“‹ Task: ${finalMessage}\n`);
79
154
  process.stderr.write(`šŸ¤– Agent: ${agentRef}\n`);
80
155
  if (thread)
81
156
  process.stderr.write(`šŸ”— Thread: ${thread}\n`);
82
157
  if (provider)
83
158
  process.stderr.write(`šŸ”Œ Provider: ${provider}\n`);
159
+ if (model)
160
+ process.stderr.write(`🧠 Model: ${model}\n`);
84
161
  if (outputFormat)
85
162
  process.stderr.write(`šŸ“„ Output-format: ${outputFormat}\n`);
86
163
  if (effort)
@@ -96,50 +173,120 @@ async function handleQuery(args) {
96
173
  catch (err) {
97
174
  const msg = err instanceof Error ? err.message : String(err);
98
175
  process.stderr.write(`Error: ${msg}\n`);
176
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
99
177
  process.exit(2);
178
+ return;
179
+ }
180
+ const actor = (0, detached_1.resolveCliTaskActor)(parsedMetadata);
181
+ // Only the detached runner may adopt a pre-issued task row. Trace variables
182
+ // describe the caller chain and are never used as this task's identity.
183
+ const detachedRunner = (0, inherited_trace_1.isDetachedRunner)();
184
+ const assignedTaskId = (0, inherited_trace_1.readRunnerAssignment)();
185
+ let selfTaskId;
186
+ let taskActor;
187
+ if (assignedTaskId !== undefined) {
188
+ const taskRepo = new repository_1.TaskRepository();
189
+ const adoption = (0, detached_1.adoptAssignedTask)(taskRepo, assignedTaskId, detachedRunner);
190
+ if (adoption.kind === 'failed') {
191
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
192
+ process.exit(1);
193
+ return;
194
+ }
195
+ if (adoption.kind === 'adopted') {
196
+ selfTaskId = adoption.taskId;
197
+ taskActor = (0, detached_1.readAssignedTaskActor)(taskRepo, adoption.taskId);
198
+ }
199
+ }
200
+ // A synchronous `crewx q` runs in this same process, but this process is
201
+ // still separate from the embedded server and never sees its in-memory
202
+ // UpdateGateService (WI-CTO-20260919-004 R2 finding #1) — only the durable
203
+ // marker it writes. Reserve-then-check before calling into the SDK below,
204
+ // not after; see admitDurableTask()'s own doc comment. Never checked for
205
+ // an adopted receipt (selfTaskId set): that work was already admitted by
206
+ // whichever process first created its pending DB row.
207
+ let admission;
208
+ if (selfTaskId === undefined) {
209
+ admission = (0, sdk_1.admitDurableTask)(`direct-cli:${command}`);
210
+ if (!admission.ok) {
211
+ (0, detached_1.reportAdmissionBlocked)(admission);
212
+ (0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
213
+ process.exit(1);
214
+ return;
215
+ }
100
216
  }
101
217
  let exitCode = 0;
218
+ let result;
102
219
  try {
103
- const result = await crewx.query(agentRef, finalMessage, {
104
- provider,
105
- effort: effort || undefined,
106
- overdrive: overdrive || undefined,
107
- threadId: thread,
108
- metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
109
- vars: Object.keys(vars).length > 0 ? vars : undefined,
110
- trace: (0, inherited_trace_1.readInheritedTrace)(),
111
- });
112
- if (!result.ok) {
113
- const errMsg = result.error?.message ?? 'Query failed';
114
- console.error(errMsg);
115
- (0, write_output_1.appendError)(out, errMsg);
220
+ try {
221
+ result = await crewx.query(agentRef, finalMessage, {
222
+ provider,
223
+ model,
224
+ effort: effort || undefined,
225
+ overdrive: overdrive || undefined,
226
+ threadId: thread,
227
+ taskId: selfTaskId,
228
+ actor: selfTaskId === undefined ? actor : taskActor,
229
+ metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
230
+ vars: Object.keys(vars).length > 0 ? vars : undefined,
231
+ trace: inheritedTrace,
232
+ });
233
+ if (!result.ok) {
234
+ const errMsg = result.error?.message ?? 'Query failed';
235
+ console.error(errMsg);
236
+ (0, write_output_1.appendError)(out, errMsg);
237
+ exitCode = 1;
238
+ }
239
+ else {
240
+ if (verbose) {
241
+ process.stderr.write(`\nšŸ“Š Results from ${agentRef}:\n`);
242
+ process.stderr.write('═'.repeat(60) + '\n');
243
+ process.stderr.write('🟢 Status: Success\n');
244
+ process.stderr.write(`šŸ¤– Provider: ${result.meta.provider}\n`);
245
+ process.stderr.write(`ā±ļø Duration: ${result.meta.durationMs}ms\n`);
246
+ process.stderr.write('\nšŸ“„ Response:\n');
247
+ process.stderr.write('─'.repeat(40) + '\n');
248
+ }
249
+ (0, write_output_1.writeResult)(out, result.data);
250
+ if (verbose) {
251
+ process.stderr.write('\nāœ… Query completed successfully\n');
252
+ }
253
+ }
254
+ }
255
+ catch (err) {
256
+ const errMsg = err instanceof Error ? err.message : String(err);
257
+ console.error(`Error: ${errMsg}`);
258
+ (0, write_output_1.appendError)(out, `Error: ${errMsg}`);
116
259
  exitCode = 1;
117
260
  }
118
- else {
119
- if (verbose) {
120
- process.stderr.write(`\nšŸ“Š Results from ${agentRef}:\n`);
121
- process.stderr.write('═'.repeat(60) + '\n');
122
- process.stderr.write('🟢 Status: Success\n');
123
- process.stderr.write(`šŸ¤– Provider: ${result.meta.provider}\n`);
124
- process.stderr.write(`ā±ļø Duration: ${result.meta.durationMs}ms\n`);
125
- process.stderr.write('\nšŸ“„ Response:\n');
126
- process.stderr.write('─'.repeat(40) + '\n');
261
+ finally {
262
+ try {
263
+ await crewx.close();
127
264
  }
128
- (0, write_output_1.writeResult)(out, result.data);
129
- if (verbose) {
130
- process.stderr.write('\nāœ… Query completed successfully\n');
265
+ catch (err) {
266
+ (0, emit_trailer_1.emitDelegationResult)({
267
+ command,
268
+ trace: inheritedTrace,
269
+ taskId: result?.meta.taskId,
270
+ agentId: result?.meta.agentId,
271
+ isError: true,
272
+ state: emitState,
273
+ });
274
+ throw err;
131
275
  }
132
276
  }
133
277
  }
134
- catch (err) {
135
- const errMsg = err instanceof Error ? err.message : String(err);
136
- console.error(`Error: ${errMsg}`);
137
- (0, write_output_1.appendError)(out, `Error: ${errMsg}`);
138
- exitCode = 1;
139
- }
140
278
  finally {
141
- await crewx.close();
279
+ if (admission?.ok)
280
+ admission.release();
142
281
  }
282
+ (0, emit_trailer_1.emitDelegationResult)({
283
+ command,
284
+ trace: inheritedTrace,
285
+ taskId: result?.meta.taskId,
286
+ agentId: result?.meta.agentId,
287
+ isError: exitCode !== 0 || result?.ok === false,
288
+ state: emitState,
289
+ });
143
290
  if (exitCode !== 0)
144
291
  process.exit(exitCode);
145
292
  }