@crewx/cli 0.9.0-rc.9 → 0.9.0-rc.90
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 +2 -1
- package/dist/builtin.js +1 -0
- package/dist/commands/agent.js +0 -58
- package/dist/commands/db.d.ts +1 -0
- package/dist/commands/db.js +191 -1
- package/dist/commands/doctor.d.ts +53 -0
- package/dist/commands/doctor.js +391 -21
- package/dist/commands/emit-trailer.d.ts +25 -0
- package/dist/commands/emit-trailer.js +33 -0
- package/dist/commands/execute.d.ts +6 -1
- package/dist/commands/execute.js +162 -11
- 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 +60 -63
- package/dist/commands/hook/status.js +3 -3
- package/dist/commands/hook/uninstall.js +2 -2
- package/dist/commands/init.js +22 -1
- package/dist/commands/log.js +4 -3
- package/dist/commands/parse-common-flags.d.ts +5 -1
- package/dist/commands/parse-common-flags.js +6 -2
- package/dist/commands/ps.js +7 -6
- package/dist/commands/publish.d.ts +1 -0
- package/dist/commands/publish.js +290 -0
- package/dist/commands/query.d.ts +3 -1
- package/dist/commands/query.js +78 -11
- package/dist/commands/registry.js +3 -1
- package/dist/commands/result.d.ts +7 -3
- package/dist/commands/result.js +41 -6
- 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/write-output.d.ts +3 -0
- package/dist/commands/write-output.js +24 -0
- package/dist/logging.d.ts +1 -1
- package/dist/logging.js +3 -2
- package/dist/main.d.ts +3 -2
- package/dist/main.js +56 -11
- package/dist/utils/env-defaults.d.ts +2 -5
- package/dist/utils/env-defaults.js +10 -5
- package/dist/utils/sdk-compat.d.ts +24 -0
- package/dist/utils/sdk-compat.js +120 -0
- package/package.json +13 -11
package/dist/commands/execute.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Flags:
|
|
7
7
|
* --thread <name> Conversation thread name
|
|
8
8
|
* --provider <cli/xxx> Provider override
|
|
9
|
+
* --model <name> Model override (e.g. claude-sonnet-5)
|
|
9
10
|
* --metadata <json> Extra metadata JSON (double-quoted object). Propagated to events/hooks/tracing.
|
|
10
11
|
* e.g. --metadata='{"workflow_id":"wf-1"}'
|
|
11
12
|
* --verbose Debug output mode (default: raw agent response only)
|
|
@@ -13,6 +14,9 @@
|
|
|
13
14
|
* --output-format <fmt> Output format (json|text|stream-json)
|
|
14
15
|
* --effort <level> Model effort (high|medium|low)
|
|
15
16
|
* -f/--prompt-file <path> Read task body from file (bypasses cmd.exe argv truncation)
|
|
17
|
+
* --detach Re-spawn as a detached runner; print task-id and exit 0 immediately.
|
|
18
|
+
* Ignored when CREWX_TRACE_ID is already set (recursive-spawn guard) or
|
|
19
|
+
* on win32 (unsupported — exits with an error).
|
|
16
20
|
*
|
|
17
21
|
* Stdin support:
|
|
18
22
|
* Pipe or redirect content into crewx x to supply the task body via stdin.
|
|
@@ -21,25 +25,123 @@
|
|
|
21
25
|
*/
|
|
22
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
27
|
exports.handleExecute = handleExecute;
|
|
28
|
+
const child_process_1 = require("child_process");
|
|
29
|
+
const fs_1 = require("fs");
|
|
30
|
+
const path_1 = require("path");
|
|
31
|
+
const os_1 = require("os");
|
|
24
32
|
const sdk_1 = require("@crewx/sdk");
|
|
25
33
|
const parse_agent_message_1 = require("./parse-agent-message");
|
|
26
34
|
const parse_common_flags_1 = require("./parse-common-flags");
|
|
27
35
|
const resolve_prompt_1 = require("./resolve-prompt");
|
|
28
36
|
const crewx_cli_1 = require("../bootstrap/crewx-cli");
|
|
29
37
|
const inherited_trace_1 = require("../utils/inherited-trace");
|
|
38
|
+
const write_output_1 = require("./write-output");
|
|
39
|
+
const emit_trailer_1 = require("./emit-trailer");
|
|
40
|
+
/**
|
|
41
|
+
* Split `--detach` out of argv, respecting the `--` literal-args sentinel
|
|
42
|
+
* (a `--detach` appearing after `--` is message text, not the flag).
|
|
43
|
+
*/
|
|
44
|
+
function extractDetachFlag(args) {
|
|
45
|
+
const rest = [];
|
|
46
|
+
let detach = false;
|
|
47
|
+
let escapeMode = false;
|
|
48
|
+
for (const arg of args) {
|
|
49
|
+
if (!escapeMode && arg === '--') {
|
|
50
|
+
escapeMode = true;
|
|
51
|
+
rest.push(arg);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!escapeMode && arg === '--detach') {
|
|
55
|
+
detach = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
rest.push(arg);
|
|
59
|
+
}
|
|
60
|
+
return { detach, rest };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Double-detach: re-spawn this same CLI entry (minus --detach) as a detached
|
|
64
|
+
* process so it survives the parent's exit. The task-id is pre-generated here
|
|
65
|
+
* and injected as CREWX_TRACE_ID so the runner's own task row is created under
|
|
66
|
+
* this id (see handleExecute's `selfTaskId` derivation below) — it doubles as
|
|
67
|
+
* both this task's row id and the root of any further delegation it spawns.
|
|
68
|
+
*
|
|
69
|
+
* stdout contract (script-parseable): task-id only, on the first line.
|
|
70
|
+
* Everything else goes to stderr.
|
|
71
|
+
*/
|
|
72
|
+
function runDetached(filteredArgs) {
|
|
73
|
+
const taskId = (0, sdk_1.generateId)('tsk');
|
|
74
|
+
const logDir = (0, path_1.join)((0, os_1.homedir)(), '.crewx', 'logs');
|
|
75
|
+
if (!(0, fs_1.existsSync)(logDir))
|
|
76
|
+
(0, fs_1.mkdirSync)(logDir, { recursive: true });
|
|
77
|
+
const logPath = (0, path_1.join)(logDir, `${taskId}.log`);
|
|
78
|
+
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
79
|
+
const entry = process.argv[1];
|
|
80
|
+
const child = (0, child_process_1.spawn)(process.execPath, [entry, 'x', ...filteredArgs], {
|
|
81
|
+
detached: true,
|
|
82
|
+
stdio: ['ignore', logFd, logFd],
|
|
83
|
+
env: { ...process.env, CREWX_TRACE_ID: taskId },
|
|
84
|
+
});
|
|
85
|
+
(0, fs_1.closeSync)(logFd);
|
|
86
|
+
child.on('error', (err) => {
|
|
87
|
+
process.stderr.write(`Failed to spawn detached runner: ${err.message}\n`);
|
|
88
|
+
});
|
|
89
|
+
child.unref();
|
|
90
|
+
console.log(taskId);
|
|
91
|
+
process.stderr.write(`Detached task ${taskId} started (log: ${logPath}).\n`);
|
|
92
|
+
process.stderr.write(`Use \`crewx result ${taskId} --wait=N\` to wait for completion.\n`);
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
30
95
|
/**
|
|
31
96
|
* Handle `crewx execute <agentRef> <message>` command.
|
|
32
97
|
*
|
|
33
98
|
* Default output: raw agent response only (stdout).
|
|
34
99
|
* --verbose: debug info written to stderr, response to stdout.
|
|
35
100
|
*/
|
|
36
|
-
async function handleExecute(args) {
|
|
37
|
-
const {
|
|
38
|
-
|
|
39
|
-
|
|
101
|
+
async function handleExecute(args, command = 'execute', emitState = (0, emit_trailer_1.createDelegationEmitState)()) {
|
|
102
|
+
const { detach, rest: detachFilteredArgs } = extractDetachFlag(args);
|
|
103
|
+
if (detach) {
|
|
104
|
+
// Recursive-spawn guard: a CREWX_TRACE_ID already present means this
|
|
105
|
+
// process is itself running inside a traced context (either the
|
|
106
|
+
// respawned runner, or a delegated sub-call) — never chain a second
|
|
107
|
+
// detach off of it. Silently fall through to normal (synchronous) execution.
|
|
108
|
+
if (process.env['CREWX_TRACE_ID']) {
|
|
109
|
+
process.stderr.write('Note: --detach ignored (already running inside a traced context; CREWX_TRACE_ID is set).\n');
|
|
110
|
+
}
|
|
111
|
+
else if (process.platform === 'win32') {
|
|
112
|
+
console.error('Error: --detach is not supported on win32.');
|
|
113
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
|
|
114
|
+
process.exit(1);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
runDetached(detachFilteredArgs);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
let parsedFlags;
|
|
123
|
+
try {
|
|
124
|
+
parsedFlags = (0, parse_common_flags_1.parseCommonFlags)(detachFilteredArgs);
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, isError: true, state: emitState });
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
const { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = parsedFlags;
|
|
131
|
+
const inheritedTrace = (0, inherited_trace_1.readInheritedTrace)();
|
|
132
|
+
let parsedAgentRef;
|
|
133
|
+
let message;
|
|
134
|
+
let finalMessage;
|
|
135
|
+
try {
|
|
136
|
+
({ agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest));
|
|
137
|
+
// Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
|
|
138
|
+
finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
40
144
|
const agentRef = parsedAgentRef || '@crewx';
|
|
41
|
-
// Resolve final prompt: argv | stdin pipe | --prompt-file (or combination)
|
|
42
|
-
const finalMessage = await (0, resolve_prompt_1.resolvePrompt)(message, promptFile);
|
|
43
145
|
if (!finalMessage) {
|
|
44
146
|
console.error('Usage: crewx execute [@agent] <task> [options]');
|
|
45
147
|
console.error(' crewx x [@agent] <task> [options]');
|
|
@@ -55,6 +157,7 @@ async function handleExecute(args) {
|
|
|
55
157
|
console.error('Options:');
|
|
56
158
|
console.error(' --thread <name> Conversation thread name');
|
|
57
159
|
console.error(' --provider <cli/xxx> Provider override');
|
|
160
|
+
console.error(' --model <name> Model override (e.g. claude-sonnet-5)');
|
|
58
161
|
console.error(' --metadata <json> Extra metadata (JSON object, double-quoted).');
|
|
59
162
|
console.error(' Propagated to events/hooks/tracing.');
|
|
60
163
|
console.error(' Invalid JSON aborts with exit code 2.');
|
|
@@ -62,16 +165,26 @@ async function handleExecute(args) {
|
|
|
62
165
|
console.error(' --verbose Debug output mode');
|
|
63
166
|
console.error(' --config/-c <path> Config file path');
|
|
64
167
|
console.error(' --output-format <fmt> Output format (json|text|stream-json)');
|
|
168
|
+
console.error(' --out/-o <path> Save result to file (stdout suppressed)');
|
|
65
169
|
console.error(' --effort <level> Model effort (high|medium|low)');
|
|
66
170
|
console.error(' -f/--prompt-file <path> Read task body from file');
|
|
67
171
|
console.error(' --var key=value Template variable (repeatable)');
|
|
68
172
|
console.error(' --overdrive Activate overdrive (boost) profile for this request');
|
|
173
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
69
174
|
process.exit(1);
|
|
175
|
+
return;
|
|
70
176
|
}
|
|
71
177
|
const configPath = config ?? process.env.CREWX_CONFIG ?? 'crewx.yaml';
|
|
72
178
|
// Only show exec audit span JSON in verbose mode
|
|
73
179
|
(0, sdk_1.setAuditVerbose)(verbose);
|
|
74
|
-
|
|
180
|
+
let crewx;
|
|
181
|
+
try {
|
|
182
|
+
crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
75
188
|
// file:// remote agent delegation is handled transparently inside Crewx.query/execute.
|
|
76
189
|
if (verbose) {
|
|
77
190
|
process.stderr.write(`📋 Task: ${finalMessage}\n`);
|
|
@@ -80,6 +193,8 @@ async function handleExecute(args) {
|
|
|
80
193
|
process.stderr.write(`🔗 Thread: ${thread}\n`);
|
|
81
194
|
if (provider)
|
|
82
195
|
process.stderr.write(`🔌 Provider: ${provider}\n`);
|
|
196
|
+
if (model)
|
|
197
|
+
process.stderr.write(`🧠 Model: ${model}\n`);
|
|
83
198
|
if (outputFormat)
|
|
84
199
|
process.stderr.write(`📄 Output-format: ${outputFormat}\n`);
|
|
85
200
|
if (effort)
|
|
@@ -95,22 +210,36 @@ async function handleExecute(args) {
|
|
|
95
210
|
catch (err) {
|
|
96
211
|
const msg = err instanceof Error ? err.message : String(err);
|
|
97
212
|
process.stderr.write(`Error: ${msg}\n`);
|
|
213
|
+
(0, emit_trailer_1.emitDelegationResult)({ command, trace: inheritedTrace, isError: true, state: emitState });
|
|
98
214
|
process.exit(2);
|
|
215
|
+
return;
|
|
99
216
|
}
|
|
217
|
+
// A trace with a rootTraceId but no parentTaskId means the id was pre-assigned
|
|
218
|
+
// to *this* task itself (the detach runner's parent injects only CREWX_TRACE_ID,
|
|
219
|
+
// never CREWX_PARENT_TASK_ID — see execute.ts's runDetached), not inherited from
|
|
220
|
+
// an ancestor task in a delegation chain (which always carries both). It doubles
|
|
221
|
+
// as this task's own row id so `crewx result <task-id>` can find it.
|
|
222
|
+
const selfTaskId = inheritedTrace && !inheritedTrace.parentTaskId
|
|
223
|
+
? (inheritedTrace.rootTraceId || undefined)
|
|
224
|
+
: undefined;
|
|
100
225
|
let exitCode = 0;
|
|
226
|
+
let result;
|
|
101
227
|
try {
|
|
102
|
-
|
|
228
|
+
result = await crewx.execute(agentRef, finalMessage, {
|
|
103
229
|
provider,
|
|
230
|
+
model,
|
|
104
231
|
effort: effort || undefined,
|
|
105
232
|
overdrive: overdrive || undefined,
|
|
106
233
|
threadId: thread,
|
|
234
|
+
taskId: selfTaskId,
|
|
107
235
|
metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
|
|
108
236
|
vars: Object.keys(vars).length > 0 ? vars : undefined,
|
|
109
|
-
trace:
|
|
237
|
+
trace: inheritedTrace,
|
|
110
238
|
});
|
|
111
239
|
if (!result.ok) {
|
|
112
240
|
const errMsg = result.error?.message ?? 'Execute failed';
|
|
113
241
|
console.error(errMsg);
|
|
242
|
+
(0, write_output_1.appendError)(out, errMsg);
|
|
114
243
|
exitCode = 1;
|
|
115
244
|
}
|
|
116
245
|
else {
|
|
@@ -123,7 +252,7 @@ async function handleExecute(args) {
|
|
|
123
252
|
process.stderr.write('\n📄 Response:\n');
|
|
124
253
|
process.stderr.write('─'.repeat(40) + '\n');
|
|
125
254
|
}
|
|
126
|
-
|
|
255
|
+
(0, write_output_1.writeResult)(out, result.data);
|
|
127
256
|
if (verbose) {
|
|
128
257
|
process.stderr.write('\n✅ Execute completed successfully\n');
|
|
129
258
|
}
|
|
@@ -132,11 +261,33 @@ async function handleExecute(args) {
|
|
|
132
261
|
catch (err) {
|
|
133
262
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
134
263
|
console.error(`Error: ${errMsg}`);
|
|
264
|
+
(0, write_output_1.appendError)(out, `Error: ${errMsg}`);
|
|
135
265
|
exitCode = 1;
|
|
136
266
|
}
|
|
137
267
|
finally {
|
|
138
|
-
|
|
268
|
+
try {
|
|
269
|
+
await crewx.close();
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
(0, emit_trailer_1.emitDelegationResult)({
|
|
273
|
+
command,
|
|
274
|
+
trace: inheritedTrace,
|
|
275
|
+
taskId: result?.meta.taskId,
|
|
276
|
+
agentId: result?.meta.agentId,
|
|
277
|
+
isError: true,
|
|
278
|
+
state: emitState,
|
|
279
|
+
});
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
139
282
|
}
|
|
283
|
+
(0, emit_trailer_1.emitDelegationResult)({
|
|
284
|
+
command,
|
|
285
|
+
trace: inheritedTrace,
|
|
286
|
+
taskId: result?.meta.taskId,
|
|
287
|
+
agentId: result?.meta.agentId,
|
|
288
|
+
isError: exitCode !== 0 || result?.ok === false,
|
|
289
|
+
state: emitState,
|
|
290
|
+
});
|
|
140
291
|
if (exitCode !== 0)
|
|
141
292
|
process.exit(exitCode);
|
|
142
293
|
}
|
|
@@ -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,13 @@ function getProviderConfigs(projectRoot, providers, crewxBin) {
|
|
|
63
28
|
}));
|
|
64
29
|
}
|
|
65
30
|
function resolveCrewxBinary() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const which = cp.execSync('which crewx 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
70
|
-
const isTmpProxy = which.includes('/T/crewx-proxy-') ||
|
|
71
|
-
which.includes('/tmp/crewx-proxy-') ||
|
|
72
|
-
/\/[Tt]e?mp\//.test(which);
|
|
73
|
-
if (which && !isTmpProxy)
|
|
74
|
-
return which;
|
|
31
|
+
const resolution = (0, sdk_1.resolveCrewxExecutable)();
|
|
32
|
+
if (!resolution.ok) {
|
|
33
|
+
throw new Error((0, sdk_1.formatCrewxExecutableFailure)(resolution));
|
|
75
34
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if ((0, fs_1.existsSync)(scriptPath)) {
|
|
80
|
-
return `${process.execPath} ${scriptPath}`;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return 'crewx';
|
|
35
|
+
const stableEntrypoint = (0, sdk_1.materializeCrewxStableEntrypoint)(resolution);
|
|
36
|
+
const useStable = stableEntrypoint && resolution.argv.some(sdk_1.isRotatingInstallPath);
|
|
37
|
+
return (0, sdk_1.formatCrewxExecutableArgv)(useStable ? [stableEntrypoint] : resolution.argv);
|
|
84
38
|
}
|
|
85
39
|
function readSettingsTyped(settingsPath) {
|
|
86
40
|
return (0, paths_1.readSettings)(settingsPath);
|
|
@@ -88,10 +42,19 @@ function readSettingsTyped(settingsPath) {
|
|
|
88
42
|
function findCrewxEntry(preToolUse) {
|
|
89
43
|
if (!preToolUse)
|
|
90
44
|
return -1;
|
|
91
|
-
return preToolUse.findIndex((entry) => entry.hooks?.some((h) => h.command
|
|
45
|
+
return preToolUse.findIndex((entry) => entry.hooks?.some((h) => command_marker_1.CREWX_HOOK_COMMAND_RE.test(h.command)));
|
|
46
|
+
}
|
|
47
|
+
function findCrewxHook(entry) {
|
|
48
|
+
if (!entry.hooks)
|
|
49
|
+
return undefined;
|
|
50
|
+
const index = entry.hooks.findIndex((hook) => command_marker_1.CREWX_HOOK_COMMAND_RE.test(hook.command));
|
|
51
|
+
if (index < 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
return { index, command: entry.hooks[index].command };
|
|
92
54
|
}
|
|
93
55
|
function installForProvider(projectRoot, config, crewxBin, yes) {
|
|
94
56
|
const { settingsPath, provider, matcher, commandArg } = config;
|
|
57
|
+
const command = `${crewxBin} hook-dispatch ${commandArg}`;
|
|
95
58
|
const settings = readSettingsTyped(settingsPath);
|
|
96
59
|
if (!settings.hooks)
|
|
97
60
|
settings.hooks = {};
|
|
@@ -99,8 +62,28 @@ function installForProvider(projectRoot, config, crewxBin, yes) {
|
|
|
99
62
|
settings.hooks.PreToolUse = [];
|
|
100
63
|
const existingIdx = findCrewxEntry(settings.hooks.PreToolUse);
|
|
101
64
|
if (existingIdx >= 0) {
|
|
102
|
-
|
|
103
|
-
|
|
65
|
+
const existingEntry = settings.hooks.PreToolUse[existingIdx];
|
|
66
|
+
const existingHook = findCrewxHook(existingEntry);
|
|
67
|
+
if (existingHook?.command === command) {
|
|
68
|
+
console.log(`[crewx] Hook already installed in ${settingsPath} (${provider})`);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (!yes) {
|
|
72
|
+
console.log(`[crewx] Existing CrewX hook needs an install-path refresh in ${settingsPath} (${provider})`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if ((0, fs_1.existsSync)(settingsPath)) {
|
|
76
|
+
const backupPath = settingsPath + '.crewx-backup';
|
|
77
|
+
(0, fs_1.copyFileSync)(settingsPath, backupPath);
|
|
78
|
+
console.log(`[crewx] Backup created: ${backupPath}`);
|
|
79
|
+
}
|
|
80
|
+
if (existingHook) {
|
|
81
|
+
existingEntry.hooks[existingHook.index] = { ...existingEntry.hooks[existingHook.index], command };
|
|
82
|
+
}
|
|
83
|
+
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
84
|
+
console.log(`[crewx] Hook updated (${provider}): ${command}`);
|
|
85
|
+
console.log(`[crewx] Settings: ${settingsPath}`);
|
|
86
|
+
return true;
|
|
104
87
|
}
|
|
105
88
|
if (!yes) {
|
|
106
89
|
console.log(`⚠️ crewx hook install will register crewx-hook-dispatch as a PreToolUse hook.\n` +
|
|
@@ -119,7 +102,6 @@ function installForProvider(projectRoot, config, crewxBin, yes) {
|
|
|
119
102
|
else {
|
|
120
103
|
(0, paths_1.ensureCodexHooks)(projectRoot);
|
|
121
104
|
}
|
|
122
|
-
const command = `${crewxBin} hook-dispatch ${commandArg}`;
|
|
123
105
|
settings.hooks.PreToolUse.push({
|
|
124
106
|
matcher,
|
|
125
107
|
hooks: [{ type: 'command', command }],
|
|
@@ -133,8 +115,15 @@ async function handleHookInstall(argsOrOpts) {
|
|
|
133
115
|
if (!Array.isArray(argsOrOpts)) {
|
|
134
116
|
const { projectRoot, yes, provider } = argsOrOpts;
|
|
135
117
|
const providers = (0, paths_1.providersFromFilter)(provider ?? 'all');
|
|
136
|
-
|
|
137
|
-
|
|
118
|
+
let crewxBin;
|
|
119
|
+
try {
|
|
120
|
+
crewxBin = resolveCrewxBinary();
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error(`[crewx] ${error instanceof Error ? error.message : String(error)}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const configs = getProviderConfigs(projectRoot, providers);
|
|
138
127
|
for (const config of configs) {
|
|
139
128
|
installForProvider(projectRoot, config, crewxBin, yes);
|
|
140
129
|
}
|
|
@@ -152,8 +141,16 @@ async function handleHookInstall(argsOrOpts) {
|
|
|
152
141
|
}
|
|
153
142
|
const providerFilter = (0, paths_1.parseProviderArg)(args);
|
|
154
143
|
const providers = (0, paths_1.providersFromFilter)(providerFilter);
|
|
155
|
-
|
|
156
|
-
|
|
144
|
+
let crewxBin;
|
|
145
|
+
try {
|
|
146
|
+
crewxBin = resolveCrewxBinary();
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error(`[crewx] ${error instanceof Error ? error.message : String(error)}`);
|
|
150
|
+
process.exitCode = 1;
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const configs = getProviderConfigs(projectRoot, providers);
|
|
157
154
|
if (!yes) {
|
|
158
155
|
const targets = configs.map((c) => ` - ${c.provider}: ${c.settingsPath}`).join('\n');
|
|
159
156
|
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.js
CHANGED
|
@@ -52,6 +52,7 @@ const git = __importStar(require("isomorphic-git"));
|
|
|
52
52
|
const nodeFs = __importStar(require("fs"));
|
|
53
53
|
const os_1 = __importDefault(require("os"));
|
|
54
54
|
const repository_1 = require("@crewx/sdk/repository");
|
|
55
|
+
const sdk_1 = require("@crewx/sdk");
|
|
55
56
|
const install_1 = require("./hook/install");
|
|
56
57
|
const STATUSLINE_SCRIPT_NAME = 'claude-usage-statusline.js';
|
|
57
58
|
const STATUSLINE_SCRIPT_MARKER = 'CrewX claude-usage-statusline';
|
|
@@ -84,6 +85,11 @@ const CREWX_MARKER = '# CrewX runtime';
|
|
|
84
85
|
const CREWX_GITIGNORE = `# CrewX runtime
|
|
85
86
|
.crewx/
|
|
86
87
|
|
|
88
|
+
# Secrets (workspace .env — see docs/manual)
|
|
89
|
+
.env
|
|
90
|
+
.env.*
|
|
91
|
+
!.env.example
|
|
92
|
+
|
|
87
93
|
# Memory runtime state (regenerable from entries/)
|
|
88
94
|
memory/*/.dirty-summary
|
|
89
95
|
memory/*/graph.json
|
|
@@ -435,7 +441,7 @@ async function handleInit(opts) {
|
|
|
435
441
|
}
|
|
436
442
|
}
|
|
437
443
|
// Always create docs dirs and templates, regardless of whether yaml was skipped
|
|
438
|
-
for (const dir of ['.crewx/logs', '.claude/commands', 'docs/goal', 'docs/daily', 'docs/wi']) {
|
|
444
|
+
for (const dir of ['.crewx/logs', '.claude/commands', 'docs/goal', 'docs/daily', 'docs/wi', 'workflows']) {
|
|
439
445
|
try {
|
|
440
446
|
(0, fs_1.mkdirSync)((0, path_1.join)(target, dir), { recursive: true });
|
|
441
447
|
}
|
|
@@ -456,6 +462,21 @@ async function handleInit(opts) {
|
|
|
456
462
|
console.log(`✅ ${t.dir}/ ${action} (${t.file})`);
|
|
457
463
|
}
|
|
458
464
|
}
|
|
465
|
+
try {
|
|
466
|
+
const workflowTemplatesDir = (0, sdk_1.resolveWorkflowTemplatesPath)();
|
|
467
|
+
for (const filename of [sdk_1.WI_DEFAULT_FLOW_FILENAME, sdk_1.WI_PLAN_FLOW_FILENAME]) {
|
|
468
|
+
const sourcePath = (0, path_1.join)(workflowTemplatesDir, filename);
|
|
469
|
+
const targetPath = (0, path_1.join)(target, 'workflows', filename);
|
|
470
|
+
if (force || !(0, fs_1.existsSync)(targetPath)) {
|
|
471
|
+
const action = (0, fs_1.existsSync)(targetPath) ? 'updated' : 'created';
|
|
472
|
+
(0, fs_1.copyFileSync)(sourcePath, targetPath);
|
|
473
|
+
console.log(`✅ workflows/ ${action} (${filename})`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
catch (e) {
|
|
478
|
+
errors.push(`WORKFLOW_TEMPLATE_FAILED:${e.message}`);
|
|
479
|
+
}
|
|
459
480
|
// Always register workspace in ~/.crewx/crewx.db (best-effort, idempotent)
|
|
460
481
|
let workspaceId;
|
|
461
482
|
let slug;
|
package/dist/commands/log.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.handleLog = handleLog;
|
|
13
|
+
const sdk_1 = require("@crewx/sdk");
|
|
13
14
|
const repository_1 = require("@crewx/sdk/repository");
|
|
14
15
|
function statusIcon(status) {
|
|
15
16
|
switch (status) {
|
|
@@ -36,10 +37,10 @@ async function handleLog(args) {
|
|
|
36
37
|
console.log(`Status: ${statusIcon(task.status)} ${task.status}`);
|
|
37
38
|
console.log(`Agent: ${task.agent_id ?? '—'}`);
|
|
38
39
|
console.log(`Mode: ${task.mode ?? '—'}`);
|
|
39
|
-
console.log(`Started: ${new Date(task.started_at)
|
|
40
|
+
console.log(`Started: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.started_at))}`);
|
|
40
41
|
if (task.completed_at) {
|
|
41
42
|
const duration = new Date(task.completed_at).getTime() - new Date(task.started_at).getTime();
|
|
42
|
-
console.log(`Completed: ${new Date(task.completed_at)
|
|
43
|
+
console.log(`Completed: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.completed_at))} (${duration}ms)`);
|
|
43
44
|
}
|
|
44
45
|
console.log('='.repeat(60));
|
|
45
46
|
console.log('');
|
|
@@ -78,7 +79,7 @@ async function handleLog(args) {
|
|
|
78
79
|
: 'running...';
|
|
79
80
|
console.log(`${idx + 1}. ${icon} ${task.id}`);
|
|
80
81
|
console.log(` Agent: ${task.agent_id ?? '—'} Mode: ${task.mode ?? '—'}`);
|
|
81
|
-
console.log(` Started: ${new Date(task.started_at)
|
|
82
|
+
console.log(` Started: ${(0, sdk_1.formatDisplayTimestamp)(new Date(task.started_at))}`);
|
|
82
83
|
console.log(` Duration: ${duration}`);
|
|
83
84
|
console.log('');
|
|
84
85
|
});
|
|
@@ -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, --metadata, --verbose, --config/-c,
|
|
5
|
+
* Handles: --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
|
|
@@ -14,6 +14,8 @@ export interface CommonFlags {
|
|
|
14
14
|
thread?: string;
|
|
15
15
|
/** Provider override (e.g., cli/claude). */
|
|
16
16
|
provider?: string;
|
|
17
|
+
/** Model override (e.g., claude-sonnet-5). */
|
|
18
|
+
model?: string;
|
|
17
19
|
/** Raw metadata JSON string. */
|
|
18
20
|
metadata?: string;
|
|
19
21
|
/** Enable verbose/debug output mode. */
|
|
@@ -28,6 +30,8 @@ export interface CommonFlags {
|
|
|
28
30
|
promptFile?: string;
|
|
29
31
|
/** Whether overdrive (boost) is active for this request. */
|
|
30
32
|
overdrive: boolean;
|
|
33
|
+
/** Output file path for saving result (--out/-o). */
|
|
34
|
+
out?: string;
|
|
31
35
|
/** Template variables from --var key=value flags. */
|
|
32
36
|
vars: Record<string, string>;
|
|
33
37
|
/** Remaining non-flag positional arguments. */
|
|
@@ -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, --metadata, --verbose, --config/-c,
|
|
6
|
+
* Handles: --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
|
|
@@ -63,11 +63,13 @@ exports.UnknownOptionError = UnknownOptionError;
|
|
|
63
63
|
function parseCommonFlags(args) {
|
|
64
64
|
const thread = parseFlag(args, '--thread');
|
|
65
65
|
const provider = parseFlag(args, '--provider');
|
|
66
|
+
const model = parseFlag(args, '--model');
|
|
66
67
|
const metadata = parseFlag(args, '--metadata');
|
|
67
68
|
const config = parseFlag(args, '--config', '-c');
|
|
68
69
|
const outputFormat = parseFlag(args, '--output-format');
|
|
69
70
|
const effort = parseFlag(args, '--effort');
|
|
70
71
|
const promptFile = parseFlag(args, '--prompt-file', '-f');
|
|
72
|
+
const out = parseFlag(args, '--out', '-o');
|
|
71
73
|
const verbose = hasFlag(args, '--verbose');
|
|
72
74
|
const overdrive = hasFlag(args, '--overdrive');
|
|
73
75
|
// Collect consumed positions for known flags
|
|
@@ -75,11 +77,13 @@ function parseCommonFlags(args) {
|
|
|
75
77
|
const flagPairs = [
|
|
76
78
|
{ names: ['--thread'] },
|
|
77
79
|
{ names: ['--provider'] },
|
|
80
|
+
{ names: ['--model'] },
|
|
78
81
|
{ names: ['--metadata'] },
|
|
79
82
|
{ names: ['--config', '-c'] },
|
|
80
83
|
{ names: ['--output-format'] },
|
|
81
84
|
{ names: ['--effort'] },
|
|
82
85
|
{ names: ['--prompt-file', '-f'] },
|
|
86
|
+
{ names: ['--out', '-o'] },
|
|
83
87
|
];
|
|
84
88
|
// Parse --var key=value flags (multiple allowed; last value wins on duplicate keys)
|
|
85
89
|
const vars = {};
|
|
@@ -153,7 +157,7 @@ function parseCommonFlags(args) {
|
|
|
153
157
|
}
|
|
154
158
|
rest.push(token);
|
|
155
159
|
}
|
|
156
|
-
return { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, rest };
|
|
160
|
+
return { thread, provider, model, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest };
|
|
157
161
|
}
|
|
158
162
|
/**
|
|
159
163
|
* Parse metadata JSON string from --metadata flag.
|