@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.
- package/dist/bootstrap/codex-writable-roots.d.ts +13 -0
- package/dist/bootstrap/codex-writable-roots.js +25 -0
- package/dist/bootstrap/crewx-cli.js +27 -6
- package/dist/builtin.d.ts +1 -1
- package/dist/builtin.js +7 -2
- package/dist/commands/agent.js +1 -107
- package/dist/commands/cal.d.ts +15 -0
- package/dist/commands/cal.js +44 -0
- package/dist/commands/db.d.ts +3 -0
- package/dist/commands/db.js +533 -1
- package/dist/commands/detached.d.ts +95 -0
- package/dist/commands/detached.js +620 -0
- package/dist/commands/doctor.d.ts +77 -0
- package/dist/commands/doctor.js +475 -22
- package/dist/commands/emit-trailer.d.ts +25 -0
- package/dist/commands/emit-trailer.js +33 -0
- package/dist/commands/execute.d.ts +8 -4
- package/dist/commands/execute.js +200 -51
- package/dist/commands/hook/command-marker.d.ts +2 -0
- package/dist/commands/hook/command-marker.js +5 -0
- package/dist/commands/hook/install.d.ts +0 -1
- package/dist/commands/hook/install.js +75 -61
- package/dist/commands/hook/status.js +3 -3
- package/dist/commands/hook/uninstall.js +2 -2
- package/dist/commands/init.d.ts +34 -0
- package/dist/commands/init.js +132 -59
- package/dist/commands/kill.d.ts +2 -8
- package/dist/commands/kill.js +6 -47
- package/dist/commands/log.js +4 -3
- package/dist/commands/parse-common-flags.d.ts +5 -1
- package/dist/commands/parse-common-flags.js +35 -4
- package/dist/commands/ps.d.ts +2 -2
- package/dist/commands/ps.js +25 -15
- package/dist/commands/publish.d.ts +1 -0
- package/dist/commands/publish.js +303 -0
- package/dist/commands/query.d.ts +7 -2
- package/dist/commands/query.js +195 -48
- package/dist/commands/registry.js +4 -2
- package/dist/commands/resolve-prompt.js +23 -1
- package/dist/commands/resolve-recipient.d.ts +14 -0
- package/dist/commands/resolve-recipient.js +47 -0
- package/dist/commands/restart.d.ts +3 -1
- package/dist/commands/restart.js +14 -4
- package/dist/commands/result.d.ts +8 -3
- package/dist/commands/result.js +149 -10
- package/dist/commands/shortcut.d.ts +1 -0
- package/dist/commands/shortcut.js +267 -0
- package/dist/commands/slack.js +2 -1
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.js +119 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -1
- package/dist/logging.d.ts +1 -1
- package/dist/logging.js +3 -2
- package/dist/main.d.ts +3 -2
- package/dist/main.js +99 -24
- package/dist/utils/env-defaults.d.ts +2 -5
- package/dist/utils/env-defaults.js +10 -5
- package/dist/utils/inherited-trace.d.ts +24 -0
- package/dist/utils/inherited-trace.js +40 -0
- package/dist/utils/product-package-root.d.ts +6 -0
- package/dist/utils/product-package-root.js +39 -0
- package/dist/utils/sdk-compat.d.ts +24 -0
- package/dist/utils/sdk-compat.js +120 -0
- package/package.json +16 -12
package/dist/commands/execute.js
CHANGED
|
@@ -4,58 +4,125 @@
|
|
|
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
|
|
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
|
-
* e.g. --metadata='{"workflow_id":"wf-1"}'
|
|
11
12
|
* --verbose Debug output mode (default: raw agent response only)
|
|
12
13
|
* --config/-c <path> Config file path override
|
|
13
14
|
* --output-format <fmt> Output format (json|text|stream-json)
|
|
14
15
|
* --effort <level> Model effort (high|medium|low)
|
|
15
|
-
* -f/--prompt-file <path> Read task body from file
|
|
16
|
+
* -f/--prompt-file <path> Read task body from file
|
|
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 x to supply the task body via stdin.
|
|
19
|
-
* e.g. cat task.md | crewx x "@agent label"
|
|
20
22
|
* Stdin is ignored when running in an interactive TTY.
|
|
21
23
|
*/
|
|
22
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.runDetachedMiddle = void 0;
|
|
23
26
|
exports.handleExecute = handleExecute;
|
|
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");
|
|
31
|
+
const resolve_recipient_1 = require("./resolve-recipient");
|
|
27
32
|
const resolve_prompt_1 = require("./resolve-prompt");
|
|
28
33
|
const crewx_cli_1 = require("../bootstrap/crewx-cli");
|
|
29
34
|
const inherited_trace_1 = require("../utils/inherited-trace");
|
|
30
35
|
const write_output_1 = require("./write-output");
|
|
36
|
+
const detached_1 = require("./detached");
|
|
37
|
+
const emit_trailer_1 = require("./emit-trailer");
|
|
38
|
+
// Exported for platform-focused tests; the middle hop remains an internal CLI
|
|
39
|
+
// contract and is not routed through the public command registry.
|
|
40
|
+
var detached_2 = require("./detached");
|
|
41
|
+
Object.defineProperty(exports, "runDetachedMiddle", { enumerable: true, get: function () { return detached_2.runDetachedMiddle; } });
|
|
31
42
|
/**
|
|
32
43
|
* Handle `crewx execute <agentRef> <message>` command.
|
|
33
44
|
*
|
|
34
45
|
* Default output: raw agent response only (stdout).
|
|
35
46
|
* --verbose: debug info written to stderr, response to stdout.
|
|
36
47
|
*/
|
|
37
|
-
async function handleExecute(args) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
async function handleExecute(args, command = 'execute', emitState = (0, emit_trailer_1.createDelegationEmitState)()) {
|
|
49
|
+
// The private middle-hop marker is consumed before public flag parsing. An
|
|
50
|
+
// invalid direct use remains an unknown option and is rejected normally.
|
|
51
|
+
const middleHop = (0, detached_1.readDetachedMiddleHop)(args);
|
|
52
|
+
if (middleHop) {
|
|
53
|
+
await (0, detached_1.runDetachedMiddle)(command, middleHop);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const receiptFlags = (0, detached_1.extractReceiptFlags)(args);
|
|
57
|
+
const receiptMode = (0, detached_1.resolveReceiptMode)({
|
|
58
|
+
detach: receiptFlags.detach,
|
|
59
|
+
wait: receiptFlags.wait,
|
|
60
|
+
detachedRunner: (0, inherited_trace_1.isDetachedRunner)(),
|
|
61
|
+
agentContext: Boolean(process.env['CREWX_AGENT_ID']),
|
|
62
|
+
});
|
|
63
|
+
if (receiptMode.receipt) {
|
|
64
|
+
await (0, detached_1.runDetached)(command, receiptFlags.rest, receiptMode.reason, args);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let parsedFlags;
|
|
68
|
+
try {
|
|
69
|
+
parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(receiptFlags.rest);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest, to } = parsedFlags;
|
|
76
|
+
const inheritedTrace = (0, inherited_trace_1.readInheritedTrace)();
|
|
77
|
+
let parsedAgentRef;
|
|
78
|
+
let message;
|
|
79
|
+
let finalMessage;
|
|
80
|
+
try {
|
|
81
|
+
({ agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest));
|
|
82
|
+
// Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
|
|
83
|
+
finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
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
|
+
}
|
|
44
107
|
if (!finalMessage) {
|
|
45
|
-
console.error('Usage: crewx execute
|
|
46
|
-
console.error(' crewx x
|
|
108
|
+
console.error('Usage: crewx execute --to <agent> <task> [options]');
|
|
109
|
+
console.error(' crewx x --to <agent> <task> [options]');
|
|
110
|
+
console.error(' Human-terminal shorthand: crewx x "@agent <task>"');
|
|
47
111
|
console.error('');
|
|
48
|
-
console.error('
|
|
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.');
|
|
49
114
|
console.error(' Task body can also be supplied via stdin pipe or --prompt-file.');
|
|
50
115
|
console.error('');
|
|
51
116
|
console.error('Examples:');
|
|
52
|
-
console.error(' crewx x
|
|
53
|
-
console.error('
|
|
54
|
-
console.error('
|
|
117
|
+
console.error(' crewx x --to agent "do something"');
|
|
118
|
+
console.error(' crewx x --to agent -f task.md');
|
|
119
|
+
console.error(' Prompt files contain only the body; set the recipient separately with --to.');
|
|
55
120
|
console.error('');
|
|
56
121
|
console.error('Options:');
|
|
122
|
+
console.error(' --to <agent> Explicit agent recipient');
|
|
57
123
|
console.error(' --thread <name> Conversation thread name');
|
|
58
124
|
console.error(' --provider <cli/xxx> Provider override');
|
|
125
|
+
console.error(' --model <name> Model override (e.g. claude-sonnet-5)');
|
|
59
126
|
console.error(' --metadata <json> Extra metadata (JSON object, double-quoted).');
|
|
60
127
|
console.error(' Propagated to events/hooks/tracing.');
|
|
61
128
|
console.error(' Invalid JSON aborts with exit code 2.');
|
|
@@ -63,30 +130,43 @@ async function handleExecute(args) {
|
|
|
63
130
|
console.error(' --verbose Debug output mode');
|
|
64
131
|
console.error(' --config/-c <path> Config file path');
|
|
65
132
|
console.error(' --output-format <fmt> Output format (json|text|stream-json)');
|
|
133
|
+
console.error(' --out/-o <path> Save result to file (stdout suppressed)');
|
|
66
134
|
console.error(' --effort <level> Model effort (high|medium|low)');
|
|
67
|
-
console.error(' -f/--prompt-file <path> Read
|
|
135
|
+
console.error(' -f/--prompt-file <path> Read body only from file; set recipient separately with --to');
|
|
68
136
|
console.error(' --var key=value Template variable (repeatable)');
|
|
69
137
|
console.error(' --overdrive Activate overdrive (boost) profile for this request');
|
|
138
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
70
139
|
process.exit(1);
|
|
140
|
+
return;
|
|
71
141
|
}
|
|
72
142
|
const configPath = config ?? process.env.CREWX_CONFIG ?? 'crewx.yaml';
|
|
73
143
|
// Only show exec audit span JSON in verbose mode
|
|
74
144
|
(0, sdk_1.setAuditVerbose)(verbose);
|
|
75
|
-
|
|
145
|
+
let crewx;
|
|
146
|
+
try {
|
|
147
|
+
crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
76
153
|
// file:// remote agent delegation is handled transparently inside Crewx.query/execute.
|
|
77
154
|
if (verbose) {
|
|
155
|
+
process.stderr.write(`mode=sync reason=${receiptMode.reason}\n`);
|
|
78
156
|
process.stderr.write(`📋 Task: ${finalMessage}\n`);
|
|
79
157
|
process.stderr.write(`🤖 Agent: ${agentRef}\n`);
|
|
80
158
|
if (thread)
|
|
81
159
|
process.stderr.write(`🔗 Thread: ${thread}\n`);
|
|
82
160
|
if (provider)
|
|
83
161
|
process.stderr.write(`🔌 Provider: ${provider}\n`);
|
|
162
|
+
if (model)
|
|
163
|
+
process.stderr.write(`🧠 Model: ${model}\n`);
|
|
84
164
|
if (outputFormat)
|
|
85
165
|
process.stderr.write(`📄 Output-format: ${outputFormat}\n`);
|
|
86
166
|
if (effort)
|
|
87
167
|
process.stderr.write(`⚡ Effort: ${effort}\n`);
|
|
88
168
|
if (overdrive)
|
|
89
|
-
process.stderr.write(
|
|
169
|
+
process.stderr.write('🚀 Overdrive: ON\n');
|
|
90
170
|
process.stderr.write('─'.repeat(60) + '\n');
|
|
91
171
|
}
|
|
92
172
|
let parsedMetadata = {};
|
|
@@ -96,50 +176,119 @@ async function handleExecute(args) {
|
|
|
96
176
|
catch (err) {
|
|
97
177
|
const msg = err instanceof Error ? err.message : String(err);
|
|
98
178
|
process.stderr.write(`Error: ${msg}\n`);
|
|
179
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
99
180
|
process.exit(2);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const actor = (0, detached_1.resolveCliTaskActor)(parsedMetadata);
|
|
184
|
+
// Only the detached runner may adopt a pre-issued task row. Trace variables
|
|
185
|
+
// describe the caller chain and are never used as this task's identity.
|
|
186
|
+
const detachedRunner = (0, inherited_trace_1.isDetachedRunner)();
|
|
187
|
+
const assignedTaskId = (0, inherited_trace_1.readRunnerAssignment)();
|
|
188
|
+
let selfTaskId;
|
|
189
|
+
let taskActor;
|
|
190
|
+
if (assignedTaskId !== undefined) {
|
|
191
|
+
const taskRepo = new repository_1.TaskRepository();
|
|
192
|
+
const adoption = (0, detached_1.adoptAssignedTask)(taskRepo, assignedTaskId, detachedRunner);
|
|
193
|
+
if (adoption.kind === 'failed') {
|
|
194
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
195
|
+
process.exit(1);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (adoption.kind === 'adopted') {
|
|
199
|
+
selfTaskId = adoption.taskId;
|
|
200
|
+
taskActor = (0, detached_1.readAssignedTaskActor)(taskRepo, adoption.taskId);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// A synchronous `crewx x` runs in this same process, but this process is
|
|
204
|
+
// still separate from the embedded server and never sees its in-memory
|
|
205
|
+
// UpdateGateService (WI-CTO-20260919-004 R2 finding #1) — only the durable
|
|
206
|
+
// marker it writes. Reserve-then-check before calling into the SDK below,
|
|
207
|
+
// not after; see admitDurableTask()'s own doc comment. Never checked for
|
|
208
|
+
// an adopted receipt (selfTaskId set): that work was already admitted by
|
|
209
|
+
// whichever process first created its pending DB row.
|
|
210
|
+
let admission;
|
|
211
|
+
if (selfTaskId === undefined) {
|
|
212
|
+
admission = (0, sdk_1.admitDurableTask)(`direct-cli:${command}`);
|
|
213
|
+
if (!admission.ok) {
|
|
214
|
+
(0, detached_1.reportAdmissionBlocked)(admission);
|
|
215
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
216
|
+
process.exit(1);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
100
219
|
}
|
|
101
220
|
let exitCode = 0;
|
|
221
|
+
let result;
|
|
102
222
|
try {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
223
|
+
try {
|
|
224
|
+
result = await crewx.execute(agentRef, finalMessage, {
|
|
225
|
+
provider,
|
|
226
|
+
model,
|
|
227
|
+
effort: effort || undefined,
|
|
228
|
+
overdrive: overdrive || undefined,
|
|
229
|
+
threadId: thread,
|
|
230
|
+
taskId: selfTaskId,
|
|
231
|
+
actor: selfTaskId === undefined ? actor : taskActor,
|
|
232
|
+
metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
|
|
233
|
+
vars: Object.keys(vars).length > 0 ? vars : undefined,
|
|
234
|
+
trace: inheritedTrace,
|
|
235
|
+
});
|
|
236
|
+
if (!result.ok) {
|
|
237
|
+
const errMsg = result.error?.message ?? 'Execute failed';
|
|
238
|
+
console.error(errMsg);
|
|
239
|
+
(0, write_output_1.appendError)(out, errMsg);
|
|
240
|
+
exitCode = 1;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
if (verbose) {
|
|
244
|
+
process.stderr.write(`\n📊 Results from ${agentRef}:\n`);
|
|
245
|
+
process.stderr.write('═'.repeat(60) + '\n');
|
|
246
|
+
process.stderr.write('🟢 Status: Success\n');
|
|
247
|
+
process.stderr.write(`🤖 Provider: ${result.meta.provider}\n`);
|
|
248
|
+
process.stderr.write(`⏱️ Duration: ${result.meta.durationMs}ms\n`);
|
|
249
|
+
process.stderr.write('\n📄 Response:\n');
|
|
250
|
+
process.stderr.write('─'.repeat(40) + '\n');
|
|
251
|
+
}
|
|
252
|
+
(0, write_output_1.writeResult)(out, result.data);
|
|
253
|
+
if (verbose)
|
|
254
|
+
process.stderr.write('\n✅ Execute completed successfully\n');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
259
|
+
console.error(`Error: ${errMsg}`);
|
|
260
|
+
(0, write_output_1.appendError)(out, `Error: ${errMsg}`);
|
|
116
261
|
exitCode = 1;
|
|
117
262
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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');
|
|
263
|
+
finally {
|
|
264
|
+
try {
|
|
265
|
+
await crewx.close();
|
|
127
266
|
}
|
|
128
|
-
(
|
|
129
|
-
|
|
130
|
-
|
|
267
|
+
catch (err) {
|
|
268
|
+
(0, emit_trailer_1.emitDelegationResult)({
|
|
269
|
+
command,
|
|
270
|
+
trace: inheritedTrace,
|
|
271
|
+
taskId: result?.meta.taskId,
|
|
272
|
+
agentId: result?.meta.agentId,
|
|
273
|
+
isError: true,
|
|
274
|
+
state: emitState,
|
|
275
|
+
});
|
|
276
|
+
throw err;
|
|
131
277
|
}
|
|
132
278
|
}
|
|
133
279
|
}
|
|
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
280
|
finally {
|
|
141
|
-
|
|
281
|
+
if (admission?.ok)
|
|
282
|
+
admission.release();
|
|
142
283
|
}
|
|
284
|
+
(0, emit_trailer_1.emitDelegationResult)({
|
|
285
|
+
command,
|
|
286
|
+
trace: inheritedTrace,
|
|
287
|
+
taskId: result?.meta.taskId,
|
|
288
|
+
agentId: result?.meta.agentId,
|
|
289
|
+
isError: exitCode !== 0 || result?.ok === false,
|
|
290
|
+
state: emitState,
|
|
291
|
+
});
|
|
143
292
|
if (exitCode !== 0)
|
|
144
293
|
process.exit(exitCode);
|
|
145
294
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CREWX_HOOK_COMMAND_RE = void 0;
|
|
4
|
+
/** Match only commands that invoke CrewX's hook-dispatch subcommand. */
|
|
5
|
+
exports.CREWX_HOOK_COMMAND_RE = /crewx(?:\.js|\.cmd|\.exe)?['"]?\s+hook-dispatch\b/i;
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* - Never traverses parent directories — project root determined by crewx.yaml
|
|
11
11
|
*/
|
|
12
12
|
import { type HookProvider } from './paths';
|
|
13
|
-
export declare const CREWX_HOOK_COMMAND_MARKER = "crewx hook-dispatch";
|
|
14
13
|
export declare function resolveCrewxBinary(): string;
|
|
15
14
|
export interface HookInstallOpts {
|
|
16
15
|
projectRoot: string;
|
|
@@ -10,49 +10,14 @@
|
|
|
10
10
|
* - Preserves existing user hooks
|
|
11
11
|
* - Never traverses parent directories — project root determined by crewx.yaml
|
|
12
12
|
*/
|
|
13
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
-
}
|
|
19
|
-
Object.defineProperty(o, k2, desc);
|
|
20
|
-
}) : (function(o, m, k, k2) {
|
|
21
|
-
if (k2 === undefined) k2 = k;
|
|
22
|
-
o[k2] = m[k];
|
|
23
|
-
}));
|
|
24
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
-
}) : function(o, v) {
|
|
27
|
-
o["default"] = v;
|
|
28
|
-
});
|
|
29
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
-
var ownKeys = function(o) {
|
|
31
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
-
var ar = [];
|
|
33
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
-
return ar;
|
|
35
|
-
};
|
|
36
|
-
return ownKeys(o);
|
|
37
|
-
};
|
|
38
|
-
return function (mod) {
|
|
39
|
-
if (mod && mod.__esModule) return mod;
|
|
40
|
-
var result = {};
|
|
41
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
-
__setModuleDefault(result, mod);
|
|
43
|
-
return result;
|
|
44
|
-
};
|
|
45
|
-
})();
|
|
46
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
-
exports.CREWX_HOOK_COMMAND_MARKER = void 0;
|
|
48
14
|
exports.resolveCrewxBinary = resolveCrewxBinary;
|
|
49
15
|
exports.handleHookInstall = handleHookInstall;
|
|
50
16
|
const fs_1 = require("fs");
|
|
51
|
-
const
|
|
52
|
-
const cp = __importStar(require("child_process"));
|
|
17
|
+
const sdk_1 = require("@crewx/sdk");
|
|
53
18
|
const paths_1 = require("./paths");
|
|
54
|
-
|
|
55
|
-
function getProviderConfigs(projectRoot, providers
|
|
19
|
+
const command_marker_1 = require("./command-marker");
|
|
20
|
+
function getProviderConfigs(projectRoot, providers) {
|
|
56
21
|
return providers.map((provider) => ({
|
|
57
22
|
settingsPath: provider === 'claude'
|
|
58
23
|
? (0, paths_1.getClaudeSettingsPath)(projectRoot)
|
|
@@ -63,24 +28,30 @@ function getProviderConfigs(projectRoot, providers, crewxBin) {
|
|
|
63
28
|
}));
|
|
64
29
|
}
|
|
65
30
|
function resolveCrewxBinary() {
|
|
66
|
-
|
|
67
|
-
|
|
31
|
+
const resolution = (0, sdk_1.resolveCrewxExecutable)();
|
|
32
|
+
if (!resolution.ok) {
|
|
33
|
+
throw new Error((0, sdk_1.formatCrewxExecutableFailure)(resolution));
|
|
34
|
+
}
|
|
35
|
+
let stableEntrypoint;
|
|
68
36
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return which;
|
|
75
|
-
}
|
|
76
|
-
catch { }
|
|
77
|
-
if (process.argv[1]) {
|
|
78
|
-
const scriptPath = (0, path_1.resolve)(process.argv[1]);
|
|
79
|
-
if ((0, fs_1.existsSync)(scriptPath)) {
|
|
80
|
-
return `${process.execPath} ${scriptPath}`;
|
|
37
|
+
stableEntrypoint = (0, sdk_1.materializeCrewxStableEntrypoint)(resolution, { allowEnvStage: true });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (resolution.requiredEnv && Object.keys(resolution.requiredEnv).length > 0) {
|
|
41
|
+
throw new Error(`CrewX hook entrypoint could not preserve required environment: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
42
|
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
if (stableEntrypoint) {
|
|
46
|
+
return (0, sdk_1.formatCrewxExecutableArgv)([stableEntrypoint]);
|
|
82
47
|
}
|
|
83
|
-
|
|
48
|
+
// A required runtime environment cannot be represented by a raw argv string,
|
|
49
|
+
// and the fingerprint shim directory is doctor-GC managed. Refuse to write
|
|
50
|
+
// either representation when the stable hook path was not eligible.
|
|
51
|
+
if (resolution.requiredEnv && Object.keys(resolution.requiredEnv).length > 0) {
|
|
52
|
+
throw new Error('CrewX hook entrypoint could not preserve required environment: stable entrypoint is not eligible for this executable');
|
|
53
|
+
}
|
|
54
|
+
return (0, sdk_1.formatCrewxExecutableArgv)(resolution.argv);
|
|
84
55
|
}
|
|
85
56
|
function readSettingsTyped(settingsPath) {
|
|
86
57
|
return (0, paths_1.readSettings)(settingsPath);
|
|
@@ -88,10 +59,19 @@ function readSettingsTyped(settingsPath) {
|
|
|
88
59
|
function findCrewxEntry(preToolUse) {
|
|
89
60
|
if (!preToolUse)
|
|
90
61
|
return -1;
|
|
91
|
-
return preToolUse.findIndex((entry) => entry.hooks?.some((h) => h.command
|
|
62
|
+
return preToolUse.findIndex((entry) => entry.hooks?.some((h) => command_marker_1.CREWX_HOOK_COMMAND_RE.test(h.command)));
|
|
63
|
+
}
|
|
64
|
+
function findCrewxHook(entry) {
|
|
65
|
+
if (!entry.hooks)
|
|
66
|
+
return undefined;
|
|
67
|
+
const index = entry.hooks.findIndex((hook) => command_marker_1.CREWX_HOOK_COMMAND_RE.test(hook.command));
|
|
68
|
+
if (index < 0)
|
|
69
|
+
return undefined;
|
|
70
|
+
return { index, command: entry.hooks[index].command };
|
|
92
71
|
}
|
|
93
72
|
function installForProvider(projectRoot, config, crewxBin, yes) {
|
|
94
73
|
const { settingsPath, provider, matcher, commandArg } = config;
|
|
74
|
+
const command = `${crewxBin} hook-dispatch ${commandArg}`;
|
|
95
75
|
const settings = readSettingsTyped(settingsPath);
|
|
96
76
|
if (!settings.hooks)
|
|
97
77
|
settings.hooks = {};
|
|
@@ -99,8 +79,28 @@ function installForProvider(projectRoot, config, crewxBin, yes) {
|
|
|
99
79
|
settings.hooks.PreToolUse = [];
|
|
100
80
|
const existingIdx = findCrewxEntry(settings.hooks.PreToolUse);
|
|
101
81
|
if (existingIdx >= 0) {
|
|
102
|
-
|
|
103
|
-
|
|
82
|
+
const existingEntry = settings.hooks.PreToolUse[existingIdx];
|
|
83
|
+
const existingHook = findCrewxHook(existingEntry);
|
|
84
|
+
if (existingHook?.command === command) {
|
|
85
|
+
console.log(`[crewx] Hook already installed in ${settingsPath} (${provider})`);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (!yes) {
|
|
89
|
+
console.log(`[crewx] Existing CrewX hook needs an install-path refresh in ${settingsPath} (${provider})`);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if ((0, fs_1.existsSync)(settingsPath)) {
|
|
93
|
+
const backupPath = settingsPath + '.crewx-backup';
|
|
94
|
+
(0, fs_1.copyFileSync)(settingsPath, backupPath);
|
|
95
|
+
console.log(`[crewx] Backup created: ${backupPath}`);
|
|
96
|
+
}
|
|
97
|
+
if (existingHook) {
|
|
98
|
+
existingEntry.hooks[existingHook.index] = { ...existingEntry.hooks[existingHook.index], command };
|
|
99
|
+
}
|
|
100
|
+
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
101
|
+
console.log(`[crewx] Hook updated (${provider}): ${command}`);
|
|
102
|
+
console.log(`[crewx] Settings: ${settingsPath}`);
|
|
103
|
+
return true;
|
|
104
104
|
}
|
|
105
105
|
if (!yes) {
|
|
106
106
|
console.log(`⚠️ crewx hook install will register crewx-hook-dispatch as a PreToolUse hook.\n` +
|
|
@@ -119,7 +119,6 @@ function installForProvider(projectRoot, config, crewxBin, yes) {
|
|
|
119
119
|
else {
|
|
120
120
|
(0, paths_1.ensureCodexHooks)(projectRoot);
|
|
121
121
|
}
|
|
122
|
-
const command = `${crewxBin} hook-dispatch ${commandArg}`;
|
|
123
122
|
settings.hooks.PreToolUse.push({
|
|
124
123
|
matcher,
|
|
125
124
|
hooks: [{ type: 'command', command }],
|
|
@@ -133,8 +132,15 @@ async function handleHookInstall(argsOrOpts) {
|
|
|
133
132
|
if (!Array.isArray(argsOrOpts)) {
|
|
134
133
|
const { projectRoot, yes, provider } = argsOrOpts;
|
|
135
134
|
const providers = (0, paths_1.providersFromFilter)(provider ?? 'all');
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
let crewxBin;
|
|
136
|
+
try {
|
|
137
|
+
crewxBin = resolveCrewxBinary();
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error(`[crewx] ${error instanceof Error ? error.message : String(error)}`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const configs = getProviderConfigs(projectRoot, providers);
|
|
138
144
|
for (const config of configs) {
|
|
139
145
|
installForProvider(projectRoot, config, crewxBin, yes);
|
|
140
146
|
}
|
|
@@ -152,8 +158,16 @@ async function handleHookInstall(argsOrOpts) {
|
|
|
152
158
|
}
|
|
153
159
|
const providerFilter = (0, paths_1.parseProviderArg)(args);
|
|
154
160
|
const providers = (0, paths_1.providersFromFilter)(providerFilter);
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
let crewxBin;
|
|
162
|
+
try {
|
|
163
|
+
crewxBin = resolveCrewxBinary();
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.error(`[crewx] ${error instanceof Error ? error.message : String(error)}`);
|
|
167
|
+
process.exitCode = 1;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const configs = getProviderConfigs(projectRoot, providers);
|
|
157
171
|
if (!yes) {
|
|
158
172
|
const targets = configs.map((c) => ` - ${c.provider}: ${c.settingsPath}`).join('\n');
|
|
159
173
|
console.log(`⚠️ crewx hook install will register crewx-hook-dispatch as a PreToolUse hook.\n` +
|
|
@@ -11,7 +11,7 @@ const fs_1 = require("fs");
|
|
|
11
11
|
const path_1 = require("path");
|
|
12
12
|
const sdk_1 = require("@crewx/sdk");
|
|
13
13
|
const paths_1 = require("./paths");
|
|
14
|
-
const
|
|
14
|
+
const command_marker_1 = require("./command-marker");
|
|
15
15
|
function showProviderStatus(projectRoot, provider) {
|
|
16
16
|
const settingsPath = provider === 'claude'
|
|
17
17
|
? (0, paths_1.getClaudeSettingsPath)(projectRoot)
|
|
@@ -26,9 +26,9 @@ function showProviderStatus(projectRoot, provider) {
|
|
|
26
26
|
try {
|
|
27
27
|
const settings = JSON.parse((0, fs_1.readFileSync)(settingsPath, 'utf8'));
|
|
28
28
|
const preHooks = settings.hooks?.PreToolUse ?? [];
|
|
29
|
-
const crewxEntry = preHooks.find((entry) => entry.hooks?.some((h) => h.command
|
|
29
|
+
const crewxEntry = preHooks.find((entry) => entry.hooks?.some((h) => command_marker_1.CREWX_HOOK_COMMAND_RE.test(h.command ?? '')));
|
|
30
30
|
if (crewxEntry) {
|
|
31
|
-
const command = crewxEntry.hooks.find((h) => h.command
|
|
31
|
+
const command = crewxEntry.hooks.find((h) => command_marker_1.CREWX_HOOK_COMMAND_RE.test(h.command ?? ''))?.command;
|
|
32
32
|
console.log(` Status: INSTALLED`);
|
|
33
33
|
console.log(` Command: ${command}`);
|
|
34
34
|
}
|
|
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.handleHookUninstall = handleHookUninstall;
|
|
11
11
|
const fs_1 = require("fs");
|
|
12
12
|
const paths_1 = require("./paths");
|
|
13
|
-
const
|
|
13
|
+
const command_marker_1 = require("./command-marker");
|
|
14
14
|
function getSettingsPath(projectRoot, provider) {
|
|
15
15
|
return provider === 'claude'
|
|
16
16
|
? (0, paths_1.getClaudeSettingsPath)(projectRoot)
|
|
@@ -35,7 +35,7 @@ function uninstallFromProvider(projectRoot, provider) {
|
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
const before = settings.hooks.PreToolUse.length;
|
|
38
|
-
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter((entry) => !entry.hooks?.some((h) => h.command
|
|
38
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter((entry) => !entry.hooks?.some((h) => command_marker_1.CREWX_HOOK_COMMAND_RE.test(h.command)));
|
|
39
39
|
const removed = settings.hooks.PreToolUse.length < before;
|
|
40
40
|
if (settings.hooks.PreToolUse.length === 0) {
|
|
41
41
|
delete settings.hooks.PreToolUse;
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -6,16 +6,48 @@
|
|
|
6
6
|
* crewx init Create crewx.yaml in current directory
|
|
7
7
|
* crewx init --force Overwrite existing configuration
|
|
8
8
|
*/
|
|
9
|
+
import { type ProviderAvailabilityDeps } from '@crewx/sdk';
|
|
10
|
+
export type InitProvider = 'cli/claude' | 'cli/codex';
|
|
11
|
+
type InitAgent = {
|
|
12
|
+
id: 'planner' | 'developer' | 'reviewer';
|
|
13
|
+
name: string;
|
|
14
|
+
role: string;
|
|
15
|
+
team: string;
|
|
16
|
+
provider: InitProvider;
|
|
17
|
+
working_directory: string;
|
|
18
|
+
description: string;
|
|
19
|
+
options: {
|
|
20
|
+
query: {
|
|
21
|
+
mode: string;
|
|
22
|
+
effort: string;
|
|
23
|
+
};
|
|
24
|
+
execute: {
|
|
25
|
+
mode: string;
|
|
26
|
+
effort: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
inline: {
|
|
30
|
+
model: string;
|
|
31
|
+
prompt: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/** Build the three default agents using the selected CLI provider profile. */
|
|
35
|
+
export declare function buildDefaultAgents(provider: InitProvider): InitAgent[];
|
|
9
36
|
export interface InitResult {
|
|
10
37
|
yamlCreated: boolean;
|
|
11
38
|
hookInstalled: boolean;
|
|
12
39
|
errors: string[];
|
|
40
|
+
provider: InitProvider;
|
|
13
41
|
skippedReason?: 'yaml-exists';
|
|
14
42
|
workspaceId?: string;
|
|
15
43
|
slug?: string;
|
|
16
44
|
statusLineConfigured?: boolean;
|
|
17
45
|
statusLineSkipped?: boolean;
|
|
18
46
|
}
|
|
47
|
+
/** Parse the provider option accepted by `crewx init`. */
|
|
48
|
+
export declare function parseInitProvider(args: readonly string[]): InitProvider | undefined;
|
|
49
|
+
/** Resolve an explicit provider or detect Claude, then Codex, using PATH only. */
|
|
50
|
+
export declare function resolveInitProvider(provider?: InitProvider, deps?: ProviderAvailabilityDeps): InitProvider;
|
|
19
51
|
/**
|
|
20
52
|
* Programmatic init — creates crewx.yaml, support dirs, and optionally installs hooks.
|
|
21
53
|
* Throws on unrecoverable errors (PATH_NOT_FOUND, YAML_CREATE_FAILED).
|
|
@@ -27,4 +59,6 @@ export declare function handleInit(opts: {
|
|
|
27
59
|
path: string;
|
|
28
60
|
skipHook?: boolean;
|
|
29
61
|
force?: boolean;
|
|
62
|
+
provider?: InitProvider;
|
|
30
63
|
}): Promise<InitResult>;
|
|
64
|
+
export {};
|