@crewx/cli 0.9.0-rc.146 → 0.9.0-rc.147
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.js +1 -113
- package/dist/bootstrap/crewx-cli.js +1 -84
- package/dist/builtin.js +5 -132
- package/dist/commands/agent.js +4 -118
- package/dist/commands/cal.js +2 -44
- package/dist/commands/db.js +4 -602
- package/dist/commands/detached.js +15 -620
- package/dist/commands/doctor.js +4 -649
- package/dist/commands/emit-trailer.js +2 -33
- package/dist/commands/execute.js +15 -294
- package/dist/commands/hook/command-marker.js +1 -5
- package/dist/commands/hook/install.js +8 -189
- package/dist/commands/hook/paths.js +1 -94
- package/dist/commands/hook/status.js +1 -86
- package/dist/commands/hook/uninstall.js +1 -71
- package/dist/commands/hook-dispatch.js +2 -180
- package/dist/commands/init.js +31 -437
- package/dist/commands/kill.js +1 -11
- package/dist/commands/log.js +3 -96
- package/dist/commands/parse-agent-message.js +1 -52
- package/dist/commands/parse-common-flags.js +3 -212
- package/dist/commands/ps.js +2 -117
- package/dist/commands/publish.js +7 -283
- package/dist/commands/query.js +16 -292
- package/dist/commands/registry.js +1 -33
- package/dist/commands/resolve-prompt.js +3 -76
- package/dist/commands/resolve-recipient.js +3 -47
- package/dist/commands/restart.js +18 -172
- package/dist/commands/result.js +1 -209
- package/dist/commands/shortcut.js +4 -253
- package/dist/commands/slack.js +16 -560
- package/dist/commands/stop.js +8 -119
- package/dist/commands/write-output.js +1 -24
- package/dist/index.js +1 -8
- package/dist/logging.js +10 -87
- package/dist/main.js +6 -289
- package/dist/register-builtin-tools.js +1 -9
- package/dist/slack/file-download.js +1 -134
- package/dist/slack/markdown.js +1 -33
- package/dist/utils/env-defaults.js +1 -15
- package/dist/utils/inherited-trace.js +1 -65
- package/dist/utils/product-package-root.js +1 -39
- package/dist/utils/sdk-compat.js +1 -120
- package/dist/utils/version.js +1 -28
- package/package.json +15 -15
package/dist/commands/doctor.js
CHANGED
|
@@ -1,649 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* crewx doctor Run full diagnosis
|
|
8
|
-
* crewx doctor --config <path> Use specific config file
|
|
9
|
-
*/
|
|
10
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.CLI_PROVIDER_INFO = void 0;
|
|
12
|
-
exports.formatBytes = formatBytes;
|
|
13
|
-
exports.inspectNpxCache = inspectNpxCache;
|
|
14
|
-
exports.inspectHookCommands = inspectHookCommands;
|
|
15
|
-
exports.inspectCrewxExecutable = inspectCrewxExecutable;
|
|
16
|
-
exports.inspectCrewxShims = inspectCrewxShims;
|
|
17
|
-
exports.checkCliProviders = checkCliProviders;
|
|
18
|
-
exports.handleDoctor = handleDoctor;
|
|
19
|
-
const node_child_process_1 = require("node:child_process");
|
|
20
|
-
const node_fs_1 = require("node:fs");
|
|
21
|
-
const node_os_1 = require("node:os");
|
|
22
|
-
const node_path_1 = require("node:path");
|
|
23
|
-
const sdk_1 = require("@crewx/sdk");
|
|
24
|
-
const parse_common_flags_1 = require("./parse-common-flags");
|
|
25
|
-
const command_marker_1 = require("./hook/command-marker");
|
|
26
|
-
/** CLI diagnostics keyed by the bare provider name used in PROVIDER_ORDER. */
|
|
27
|
-
exports.CLI_PROVIDER_INFO = {
|
|
28
|
-
codex: { cmd: 'codex', install: 'npm install -g @openai/codex' },
|
|
29
|
-
claude: { cmd: 'claude', install: 'npm install -g @anthropic-ai/claude-code' },
|
|
30
|
-
grok: { cmd: 'grok', install: 'curl -fsSL https://x.ai/cli/install.sh | bash' },
|
|
31
|
-
opencode: { cmd: 'opencode', install: 'npm install -g opencode' },
|
|
32
|
-
antigravity: { cmd: 'antigravity', install: 'npm install -g antigravity' },
|
|
33
|
-
copilot: { cmd: 'gh', install: 'brew install gh # or visit cli.github.com' },
|
|
34
|
-
};
|
|
35
|
-
function statusIcon(status) {
|
|
36
|
-
switch (status) {
|
|
37
|
-
case 'success': return '✅';
|
|
38
|
-
case 'warning': return '⚠️ ';
|
|
39
|
-
case 'error': return '❌';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Check if a CLI command is available on PATH.
|
|
44
|
-
*/
|
|
45
|
-
function isCommandAvailable(cmd) {
|
|
46
|
-
try {
|
|
47
|
-
(0, node_child_process_1.execSync)(`which ${cmd}`, { stdio: 'ignore' });
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/** Format a byte count for diagnostic output. */
|
|
55
|
-
function formatBytes(bytes) {
|
|
56
|
-
if (!Number.isFinite(bytes) || bytes <= 0)
|
|
57
|
-
return '0 B';
|
|
58
|
-
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
59
|
-
let value = bytes;
|
|
60
|
-
let unitIndex = 0;
|
|
61
|
-
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
62
|
-
value /= 1024;
|
|
63
|
-
unitIndex += 1;
|
|
64
|
-
}
|
|
65
|
-
const rounded = unitIndex === 0 || value >= 10 ? Math.round(value) : value.toFixed(1);
|
|
66
|
-
return `${rounded} ${units[unitIndex]}`;
|
|
67
|
-
}
|
|
68
|
-
function directorySize(path) {
|
|
69
|
-
try {
|
|
70
|
-
const stats = (0, node_fs_1.lstatSync)(path);
|
|
71
|
-
if (!stats.isDirectory())
|
|
72
|
-
return stats.size;
|
|
73
|
-
return (0, node_fs_1.readdirSync)(path, { withFileTypes: true }).reduce((total, entry) => {
|
|
74
|
-
return total + directorySize((0, node_path_1.join)(path, entry.name));
|
|
75
|
-
}, 0);
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return 0;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function immediateDirectories(root) {
|
|
82
|
-
try {
|
|
83
|
-
return (0, node_fs_1.readdirSync)(root, { withFileTypes: true })
|
|
84
|
-
.filter((entry) => entry.isDirectory())
|
|
85
|
-
.map((entry) => (0, node_path_1.join)(root, entry.name))
|
|
86
|
-
.sort();
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function readJsonObject(filePath) {
|
|
93
|
-
try {
|
|
94
|
-
const parsed = JSON.parse((0, node_fs_1.readFileSync)(filePath, 'utf8'));
|
|
95
|
-
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
96
|
-
return undefined;
|
|
97
|
-
return parsed;
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
return undefined;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function getNpxCacheRoot(env = process.env) {
|
|
104
|
-
const configuredCache = env.NPM_CONFIG_CACHE ?? env.npm_config_cache;
|
|
105
|
-
const npmCache = configuredCache?.trim() || (0, node_path_1.join)((0, node_os_1.homedir)(), '.npm');
|
|
106
|
-
const resolvedCache = (0, node_path_1.resolve)(npmCache);
|
|
107
|
-
return (0, node_path_1.basename)(resolvedCache) === '_npx'
|
|
108
|
-
? resolvedCache
|
|
109
|
-
: (0, node_path_1.join)(resolvedCache, '_npx');
|
|
110
|
-
}
|
|
111
|
-
const DEPENDENCY_SECTIONS = [
|
|
112
|
-
'dependencies',
|
|
113
|
-
'devDependencies',
|
|
114
|
-
'optionalDependencies',
|
|
115
|
-
'peerDependencies',
|
|
116
|
-
];
|
|
117
|
-
function staleFileDependencies(cacheDirectory) {
|
|
118
|
-
const packageJson = readJsonObject((0, node_path_1.join)(cacheDirectory, 'package.json'));
|
|
119
|
-
if (!packageJson)
|
|
120
|
-
return [];
|
|
121
|
-
const missing = [];
|
|
122
|
-
for (const section of DEPENDENCY_SECTIONS) {
|
|
123
|
-
const dependencies = packageJson[section];
|
|
124
|
-
if (typeof dependencies !== 'object' || dependencies === null || Array.isArray(dependencies))
|
|
125
|
-
continue;
|
|
126
|
-
for (const [name, specifier] of Object.entries(dependencies)) {
|
|
127
|
-
if (typeof specifier !== 'string' || !specifier.startsWith('file:'))
|
|
128
|
-
continue;
|
|
129
|
-
const target = (0, node_path_1.resolve)(cacheDirectory, specifier.slice('file:'.length));
|
|
130
|
-
if (!(0, node_fs_1.existsSync)(target)) {
|
|
131
|
-
missing.push(`${section}.${name}=${specifier} (missing ${target})`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return missing;
|
|
136
|
-
}
|
|
137
|
-
function addArtifact(artifacts, directory, details) {
|
|
138
|
-
const existing = artifacts.get(directory);
|
|
139
|
-
if (existing) {
|
|
140
|
-
existing.details.push(...details);
|
|
141
|
-
return existing;
|
|
142
|
-
}
|
|
143
|
-
const artifact = {
|
|
144
|
-
directory,
|
|
145
|
-
sizeBytes: directorySize(directory),
|
|
146
|
-
details: [...details],
|
|
147
|
-
};
|
|
148
|
-
artifacts.set(directory, artifact);
|
|
149
|
-
return artifact;
|
|
150
|
-
}
|
|
151
|
-
/** Inspect npm's persistent `_npx` cache without modifying it. */
|
|
152
|
-
function inspectNpxCache(cacheRoot = getNpxCacheRoot()) {
|
|
153
|
-
const resolvedRoot = (0, node_path_1.resolve)(cacheRoot);
|
|
154
|
-
const zeroByteLockfileDirectories = [];
|
|
155
|
-
const staleFileCacheDirectories = [];
|
|
156
|
-
const cleanable = new Map();
|
|
157
|
-
for (const directory of immediateDirectories(resolvedRoot)) {
|
|
158
|
-
const lockfile = (0, node_path_1.join)(directory, 'package-lock.json');
|
|
159
|
-
try {
|
|
160
|
-
const lockfileStats = (0, node_fs_1.statSync)(lockfile);
|
|
161
|
-
if (lockfileStats.isFile() && lockfileStats.size === 0) {
|
|
162
|
-
const entry = addArtifact(cleanable, directory, [`0-byte lockfile: ${lockfile}`]);
|
|
163
|
-
zeroByteLockfileDirectories.push(entry);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
// A cache entry can disappear while doctor is inspecting it.
|
|
168
|
-
}
|
|
169
|
-
const missing = staleFileDependencies(directory);
|
|
170
|
-
if (missing.length > 0) {
|
|
171
|
-
const entry = addArtifact(cleanable, directory, missing);
|
|
172
|
-
staleFileCacheDirectories.push(entry);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
cacheRoot: resolvedRoot,
|
|
177
|
-
zeroByteLockfileDirectories,
|
|
178
|
-
staleFileCacheDirectories,
|
|
179
|
-
cleanableDirectories: [...cleanable.values()],
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function asRecord(value) {
|
|
183
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
184
|
-
return undefined;
|
|
185
|
-
return value;
|
|
186
|
-
}
|
|
187
|
-
function hookCommandsFromSettings(settings) {
|
|
188
|
-
const hookContainer = asRecord(settings.hooks) ?? settings;
|
|
189
|
-
const preToolUse = hookContainer.PreToolUse;
|
|
190
|
-
if (!Array.isArray(preToolUse))
|
|
191
|
-
return [];
|
|
192
|
-
const commands = [];
|
|
193
|
-
for (const entry of preToolUse) {
|
|
194
|
-
const entryRecord = asRecord(entry);
|
|
195
|
-
const hooks = entryRecord?.hooks;
|
|
196
|
-
if (!Array.isArray(hooks))
|
|
197
|
-
continue;
|
|
198
|
-
for (const hook of hooks) {
|
|
199
|
-
const command = asRecord(hook)?.command;
|
|
200
|
-
if (typeof command === 'string' && command_marker_1.CREWX_HOOK_COMMAND_RE.test(command)) {
|
|
201
|
-
commands.push(command);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return commands;
|
|
206
|
-
}
|
|
207
|
-
function firstShellToken(command) {
|
|
208
|
-
const value = command.trim();
|
|
209
|
-
if (!value)
|
|
210
|
-
return '';
|
|
211
|
-
if (value.startsWith("'") || value.startsWith('"')) {
|
|
212
|
-
const quote = value[0];
|
|
213
|
-
const closing = value.indexOf(quote, 1);
|
|
214
|
-
return closing === -1 ? value.slice(1) : value.slice(1, closing);
|
|
215
|
-
}
|
|
216
|
-
return value.split(/\s+/, 1)[0];
|
|
217
|
-
}
|
|
218
|
-
function isAbsoluteExecutableToken(token) {
|
|
219
|
-
return (0, node_path_1.isAbsolute)(token) || /^[A-Za-z]:[\\/]/.test(token) || token.startsWith('\\\\');
|
|
220
|
-
}
|
|
221
|
-
function hookCommandIssue(command) {
|
|
222
|
-
const executable = firstShellToken(command);
|
|
223
|
-
if (/^(?:npx|pnpm|yarn)$/i.test(executable))
|
|
224
|
-
return 'launcher';
|
|
225
|
-
if (!isAbsoluteExecutableToken(executable))
|
|
226
|
-
return 'non-absolute';
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
|
229
|
-
/** Inspect installed CrewX hooks without rewriting provider settings. */
|
|
230
|
-
function inspectHookCommands(projectRoot) {
|
|
231
|
-
const candidates = [
|
|
232
|
-
{ provider: 'claude', settingsPath: (0, node_path_1.join)((0, node_path_1.resolve)(projectRoot), '.claude', 'settings.json') },
|
|
233
|
-
{ provider: 'codex', settingsPath: (0, node_path_1.join)((0, node_path_1.resolve)(projectRoot), '.codex', 'hooks.json') },
|
|
234
|
-
];
|
|
235
|
-
const inspectedFiles = [];
|
|
236
|
-
const issues = [];
|
|
237
|
-
for (const candidate of candidates) {
|
|
238
|
-
if (!(0, node_fs_1.existsSync)(candidate.settingsPath))
|
|
239
|
-
continue;
|
|
240
|
-
inspectedFiles.push(candidate.settingsPath);
|
|
241
|
-
const settings = readJsonObject(candidate.settingsPath);
|
|
242
|
-
if (!settings)
|
|
243
|
-
continue;
|
|
244
|
-
for (const command of hookCommandsFromSettings(settings)) {
|
|
245
|
-
const reason = hookCommandIssue(command);
|
|
246
|
-
if (reason) {
|
|
247
|
-
issues.push({
|
|
248
|
-
provider: candidate.provider,
|
|
249
|
-
settingsPath: candidate.settingsPath,
|
|
250
|
-
command,
|
|
251
|
-
reason,
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return { inspectedFiles, issues };
|
|
257
|
-
}
|
|
258
|
-
function configuredBinResolution(configPath, fallback = 'global_first') {
|
|
259
|
-
try {
|
|
260
|
-
const config = (0, sdk_1.loadYamlFile)(configPath);
|
|
261
|
-
const value = config.settings?.bin_resolution;
|
|
262
|
-
return value === 'local_first' || value === 'global_first' ? value : fallback;
|
|
263
|
-
}
|
|
264
|
-
catch {
|
|
265
|
-
return fallback;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
function readWindowsCrewxPathCandidates(cwd, env, platform) {
|
|
269
|
-
if (platform !== 'win32')
|
|
270
|
-
return [];
|
|
271
|
-
try {
|
|
272
|
-
(0, sdk_1.primeWindowsConsoleCodePage)();
|
|
273
|
-
const output = (0, node_child_process_1.execFileSync)('where.exe', ['crewx'], {
|
|
274
|
-
cwd,
|
|
275
|
-
env,
|
|
276
|
-
encoding: null,
|
|
277
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
278
|
-
});
|
|
279
|
-
return (0, sdk_1.decodeConsoleBuffer)(output, { platform })
|
|
280
|
-
.split(/\r?\n/)
|
|
281
|
-
.map((line) => line.trim())
|
|
282
|
-
.filter((line) => line.length > 0);
|
|
283
|
-
}
|
|
284
|
-
catch {
|
|
285
|
-
return [];
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
/** Inspect the SDK-selected CrewX executable without printing environment values. */
|
|
289
|
-
function inspectCrewxExecutable(options = {}) {
|
|
290
|
-
const platform = options.platform ?? process.platform;
|
|
291
|
-
const cwd = (0, node_path_1.resolve)(options.cwd ?? process.cwd());
|
|
292
|
-
const env = options.env ?? process.env;
|
|
293
|
-
const configPath = options.configPath ?? (0, node_path_1.join)(cwd, 'crewx.yaml');
|
|
294
|
-
const resolution = options.resolution ?? (0, sdk_1.resolveCrewxExecutable)({ env, cwd, platform });
|
|
295
|
-
const pathCandidates = options.pathCandidates
|
|
296
|
-
? [...options.pathCandidates]
|
|
297
|
-
: readWindowsCrewxPathCandidates(cwd, env, platform);
|
|
298
|
-
const requiredEnvKeys = resolution.ok
|
|
299
|
-
? Object.keys(resolution.requiredEnv ?? {})
|
|
300
|
-
.filter((key) => key === 'ELECTRON_RUN_AS_NODE')
|
|
301
|
-
: [];
|
|
302
|
-
return {
|
|
303
|
-
platform,
|
|
304
|
-
pathCandidates,
|
|
305
|
-
...(resolution.ok
|
|
306
|
-
? {
|
|
307
|
-
stage: resolution.stage,
|
|
308
|
-
argv: [...resolution.argv],
|
|
309
|
-
adoptedPath: resolution.argv[0],
|
|
310
|
-
}
|
|
311
|
-
: { resolutionError: resolution.reason }),
|
|
312
|
-
electron: requiredEnvKeys.includes('ELECTRON_RUN_AS_NODE'),
|
|
313
|
-
requiredEnvKeys,
|
|
314
|
-
binResolution: options.binResolution ?? configuredBinResolution(configPath),
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
function checkCrewxExecutable(report) {
|
|
318
|
-
const details = [
|
|
319
|
-
`platform: ${report.platform}`,
|
|
320
|
-
`stage: ${report.stage ?? '(unresolved)'}`,
|
|
321
|
-
`argv: ${report.argv ? (0, sdk_1.formatCrewxExecutableArgv)(report.argv) : '(unresolved)'}`,
|
|
322
|
-
`adopted path: ${report.adoptedPath ?? '(none)'}`,
|
|
323
|
-
`Electron runtime: ${report.electron ? 'yes' : 'no'}`,
|
|
324
|
-
`required environment keys: ${report.requiredEnvKeys.length > 0 ? report.requiredEnvKeys.join(', ') : '(none)'}`,
|
|
325
|
-
`bin_resolution setting: ${report.binResolution}`,
|
|
326
|
-
...(report.platform === 'win32'
|
|
327
|
-
? [`where.exe crewx candidates: ${report.pathCandidates.length > 0 ? report.pathCandidates.join(', ') : '(none)'}`]
|
|
328
|
-
: []),
|
|
329
|
-
];
|
|
330
|
-
if (report.resolutionError) {
|
|
331
|
-
return {
|
|
332
|
-
name: 'CrewX Executable',
|
|
333
|
-
status: 'warning',
|
|
334
|
-
message: 'Could not resolve the CrewX executable',
|
|
335
|
-
details: `${details.join('\n')}\nResolution failed; no environment values were printed.`,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
return {
|
|
339
|
-
name: 'CrewX Executable',
|
|
340
|
-
status: 'success',
|
|
341
|
-
message: 'SDK child executable contract is available',
|
|
342
|
-
details: details.join('\n'),
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
/** Inspect orphaned CrewX PATH shim directories without modifying them. */
|
|
346
|
-
function inspectCrewxShims(crewxHome = (0, sdk_1.getCrewxHome)(), resolvedFingerprint = (() => {
|
|
347
|
-
const resolution = (0, sdk_1.resolveCrewxExecutable)();
|
|
348
|
-
return resolution.ok ? (0, sdk_1.crewxCliShimFingerprint)(resolution) : undefined;
|
|
349
|
-
})()) {
|
|
350
|
-
const shimRoot = (0, node_path_1.join)((0, node_path_1.resolve)(crewxHome), 'bin-shim');
|
|
351
|
-
const staleDirectories = [];
|
|
352
|
-
if (resolvedFingerprint) {
|
|
353
|
-
for (const directory of immediateDirectories(shimRoot)) {
|
|
354
|
-
const fingerprint = directory.slice(shimRoot.length + 1);
|
|
355
|
-
if (fingerprint === resolvedFingerprint)
|
|
356
|
-
continue;
|
|
357
|
-
staleDirectories.push({
|
|
358
|
-
fingerprint,
|
|
359
|
-
directory,
|
|
360
|
-
sizeBytes: directorySize(directory),
|
|
361
|
-
details: [`not current fingerprint (${resolvedFingerprint})`],
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return { shimRoot, currentFingerprint: resolvedFingerprint, staleDirectories };
|
|
366
|
-
}
|
|
367
|
-
function checkNpxCache(report) {
|
|
368
|
-
const corrupt = report.zeroByteLockfileDirectories.length;
|
|
369
|
-
const stale = report.staleFileCacheDirectories.length;
|
|
370
|
-
const cleanable = report.cleanableDirectories.length;
|
|
371
|
-
if (corrupt === 0 && stale === 0) {
|
|
372
|
-
return {
|
|
373
|
-
name: 'npx Cache',
|
|
374
|
-
status: 'success',
|
|
375
|
-
message: `No stale entries found in ${report.cacheRoot}`,
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
const details = [];
|
|
379
|
-
if (corrupt > 0) {
|
|
380
|
-
details.push(`0-byte package-lock.json: ${report.zeroByteLockfileDirectories.map((entry) => entry.directory).join(', ')}`);
|
|
381
|
-
}
|
|
382
|
-
if (stale > 0) {
|
|
383
|
-
details.push(`stale file: cache: ${report.staleFileCacheDirectories.map((entry) => entry.directory).join(', ')}`);
|
|
384
|
-
}
|
|
385
|
-
details.push('Run `crewx doctor --clean-npx-cache` to remove these cache directories explicitly.');
|
|
386
|
-
return {
|
|
387
|
-
name: 'npx Cache',
|
|
388
|
-
status: 'warning',
|
|
389
|
-
message: `Found ${cleanable} problematic npx cache director${cleanable === 1 ? 'y' : 'ies'}`,
|
|
390
|
-
details: details.join('\n'),
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
function checkHookCommands(report) {
|
|
394
|
-
if (report.issues.length === 0) {
|
|
395
|
-
return {
|
|
396
|
-
name: 'CrewX Hooks',
|
|
397
|
-
status: 'success',
|
|
398
|
-
message: report.inspectedFiles.length > 0
|
|
399
|
-
? 'Installed CrewX hook commands use absolute entrypoints'
|
|
400
|
-
: 'No installed CrewX hooks found',
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
const details = report.issues
|
|
404
|
-
.map((issue) => `${issue.settingsPath}: ${issue.command} (${issue.reason})`)
|
|
405
|
-
.join('\n');
|
|
406
|
-
return {
|
|
407
|
-
name: 'CrewX Hooks',
|
|
408
|
-
status: 'warning',
|
|
409
|
-
message: `Found ${report.issues.length} hook command(s) that need an absolute CrewX entrypoint`,
|
|
410
|
-
details: `${details}\nRun \`crewx hook install\` to refresh the hook after reviewing the change.`,
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
function checkCrewxShims(report) {
|
|
414
|
-
if (!report.currentFingerprint) {
|
|
415
|
-
return {
|
|
416
|
-
name: 'CrewX PATH Shims',
|
|
417
|
-
status: 'warning',
|
|
418
|
-
message: 'Could not resolve the current CrewX executable; skipped orphan shim cleanup',
|
|
419
|
-
details: `Shim root: ${report.shimRoot}`,
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
if (report.staleDirectories.length === 0) {
|
|
423
|
-
return {
|
|
424
|
-
name: 'CrewX PATH Shims',
|
|
425
|
-
status: 'success',
|
|
426
|
-
message: 'No orphaned CrewX PATH shims found',
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
return {
|
|
430
|
-
name: 'CrewX PATH Shims',
|
|
431
|
-
status: 'warning',
|
|
432
|
-
message: `Found ${report.staleDirectories.length} orphaned CrewX PATH shim director${report.staleDirectories.length === 1 ? 'y' : 'ies'}`,
|
|
433
|
-
details: `${report.staleDirectories.map((entry) => entry.directory).join(', ')}\nRun \`crewx doctor --clean-npx-cache\` to remove stale shims explicitly.`,
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function safelyRemoveDirectories(directories, parent) {
|
|
437
|
-
const expectedParent = (0, node_path_1.resolve)(parent);
|
|
438
|
-
let removed = 0;
|
|
439
|
-
const failed = [];
|
|
440
|
-
for (const entry of directories) {
|
|
441
|
-
const directory = (0, node_path_1.resolve)(entry.directory);
|
|
442
|
-
if ((0, node_path_1.dirname)(directory) !== expectedParent) {
|
|
443
|
-
failed.push(`${directory} (unexpected parent)`);
|
|
444
|
-
continue;
|
|
445
|
-
}
|
|
446
|
-
try {
|
|
447
|
-
(0, node_fs_1.rmSync)(directory, { recursive: true, force: true });
|
|
448
|
-
if (!(0, node_fs_1.existsSync)(directory))
|
|
449
|
-
removed += 1;
|
|
450
|
-
else
|
|
451
|
-
failed.push(directory);
|
|
452
|
-
}
|
|
453
|
-
catch {
|
|
454
|
-
failed.push(directory);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
return { removed, failed };
|
|
458
|
-
}
|
|
459
|
-
function cleanDoctorArtifacts(npxCache, shims) {
|
|
460
|
-
const npxTargets = npxCache.cleanableDirectories;
|
|
461
|
-
const shimTargets = shims.staleDirectories;
|
|
462
|
-
const allTargets = [...npxTargets, ...shimTargets];
|
|
463
|
-
const totalBytes = allTargets.reduce((sum, entry) => sum + entry.sizeBytes, 0);
|
|
464
|
-
console.log(`⚠️ --clean-npx-cache: before cleanup, ${npxTargets.length} npx cache director${npxTargets.length === 1 ? 'y' : 'ies'} ` +
|
|
465
|
-
`and ${shimTargets.length} orphan shim director${shimTargets.length === 1 ? 'y' : 'ies'} (${formatBytes(totalBytes)}).`);
|
|
466
|
-
const npxResult = safelyRemoveDirectories(npxTargets, npxCache.cacheRoot);
|
|
467
|
-
const shimResult = safelyRemoveDirectories(shimTargets, shims.shimRoot);
|
|
468
|
-
const failures = [...npxResult.failed, ...shimResult.failed];
|
|
469
|
-
console.log(` Removed ${npxResult.removed + shimResult.removed} director${npxResult.removed + shimResult.removed === 1 ? 'y' : 'ies'}.`);
|
|
470
|
-
if (failures.length > 0) {
|
|
471
|
-
console.log(` Could not remove: ${failures.join(', ')}`);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* Check crewx.yaml configuration file.
|
|
476
|
-
*/
|
|
477
|
-
function checkConfig(configPath) {
|
|
478
|
-
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
479
|
-
return {
|
|
480
|
-
name: 'Configuration File',
|
|
481
|
-
status: 'error',
|
|
482
|
-
message: `Configuration file not found: ${configPath}`,
|
|
483
|
-
details: "Run 'crewx init' to create a default configuration file.",
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
try {
|
|
487
|
-
const raw = (0, sdk_1.loadYamlFile)(configPath);
|
|
488
|
-
const config = raw;
|
|
489
|
-
if (!config?.agents || !Array.isArray(config.agents)) {
|
|
490
|
-
return {
|
|
491
|
-
name: 'Configuration File',
|
|
492
|
-
status: 'error',
|
|
493
|
-
message: 'Invalid configuration format',
|
|
494
|
-
details: 'Configuration file exists but has invalid structure.',
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
if (config.agents.length === 0) {
|
|
498
|
-
return {
|
|
499
|
-
name: 'Configuration File',
|
|
500
|
-
status: 'warning',
|
|
501
|
-
message: 'No agents configured',
|
|
502
|
-
details: 'Add agent definitions to your crewx.yaml.',
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
return {
|
|
506
|
-
name: 'Configuration File',
|
|
507
|
-
status: 'success',
|
|
508
|
-
message: `Found ${config.agents.length} agent(s) in ${configPath}`,
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
catch (err) {
|
|
512
|
-
return {
|
|
513
|
-
name: 'Configuration File',
|
|
514
|
-
status: 'error',
|
|
515
|
-
message: 'Failed to parse configuration',
|
|
516
|
-
details: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Check .crewx/logs directory.
|
|
522
|
-
*/
|
|
523
|
-
function checkLogsDir() {
|
|
524
|
-
const logsDir = (0, node_path_1.join)(process.cwd(), '.crewx', 'logs');
|
|
525
|
-
if (!(0, node_fs_1.existsSync)(logsDir)) {
|
|
526
|
-
return {
|
|
527
|
-
name: 'Logs Directory',
|
|
528
|
-
status: 'warning',
|
|
529
|
-
message: 'Logs directory not found',
|
|
530
|
-
details: `Will be created automatically at ${logsDir}`,
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
return {
|
|
534
|
-
name: 'Logs Directory',
|
|
535
|
-
status: 'success',
|
|
536
|
-
message: `Logs directory exists: ${logsDir}`,
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
/**
|
|
540
|
-
* Check CLI provider availability in the canonical PROVIDER_ORDER.
|
|
541
|
-
*/
|
|
542
|
-
function checkCliProviders() {
|
|
543
|
-
return sdk_1.PROVIDER_ORDER.map(provider => {
|
|
544
|
-
const info = exports.CLI_PROVIDER_INFO[provider];
|
|
545
|
-
if (!info) {
|
|
546
|
-
return {
|
|
547
|
-
name: `${provider.toUpperCase()} CLI`,
|
|
548
|
-
status: 'error',
|
|
549
|
-
message: 'Diagnostic metadata is missing',
|
|
550
|
-
details: `Add ${provider} to CLI_PROVIDER_INFO.`,
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
const displayName = provider === 'copilot' ? 'copilot (gh)' : provider;
|
|
554
|
-
const available = isCommandAvailable(info.cmd);
|
|
555
|
-
return {
|
|
556
|
-
name: `${displayName.toUpperCase()} CLI`,
|
|
557
|
-
status: available ? 'success' : 'warning',
|
|
558
|
-
message: available ? 'Installed and available' : 'Not installed or not on PATH',
|
|
559
|
-
details: available ? undefined : `Install: ${info.install}`,
|
|
560
|
-
};
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Check CREWX_CLI env var.
|
|
565
|
-
*/
|
|
566
|
-
function checkEnvVars() {
|
|
567
|
-
const crewxCli = process.env.CREWX_CLI;
|
|
568
|
-
if (crewxCli) {
|
|
569
|
-
return {
|
|
570
|
-
name: 'CREWX_CLI',
|
|
571
|
-
status: 'success',
|
|
572
|
-
message: `Set to: ${crewxCli}`,
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
return {
|
|
576
|
-
name: 'CREWX_CLI',
|
|
577
|
-
status: 'warning',
|
|
578
|
-
message: 'Not set — defaulting to "crewx"',
|
|
579
|
-
details: 'This is auto-set at CLI startup; no action needed.',
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Assess overall health.
|
|
584
|
-
*/
|
|
585
|
-
function assessHealth(diagnostics) {
|
|
586
|
-
const errors = diagnostics.filter(d => d.status === 'error').length;
|
|
587
|
-
const warnings = diagnostics.filter(d => d.status === 'warning').length;
|
|
588
|
-
const successes = diagnostics.filter(d => d.status === 'success').length;
|
|
589
|
-
if (errors > 0) {
|
|
590
|
-
return { status: 'error', message: `System has ${errors} error(s) that need attention` };
|
|
591
|
-
}
|
|
592
|
-
if (warnings > 0) {
|
|
593
|
-
return { status: 'warning', message: `System is functional with ${warnings} warning(s)` };
|
|
594
|
-
}
|
|
595
|
-
return { status: 'success', message: `System is healthy — all ${successes} checks passed` };
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Handle `crewx doctor` command.
|
|
599
|
-
*/
|
|
600
|
-
async function handleDoctor(args) {
|
|
601
|
-
const cleanNpxCache = args.includes('--clean-npx-cache');
|
|
602
|
-
const commonArgs = args.filter((arg) => arg !== '--clean-npx-cache');
|
|
603
|
-
const { config } = (0, parse_common_flags_1.parseCommonFlags)(commonArgs);
|
|
604
|
-
const configPath = config ?? process.env.CREWX_CONFIG ?? (0, node_path_1.join)(process.cwd(), 'crewx.yaml');
|
|
605
|
-
console.log('🩺 Starting CrewX system diagnosis...\n');
|
|
606
|
-
let npxCache = inspectNpxCache();
|
|
607
|
-
let shims = inspectCrewxShims();
|
|
608
|
-
const projectRoot = (0, node_path_1.dirname)((0, node_path_1.resolve)(configPath));
|
|
609
|
-
const hooks = inspectHookCommands(projectRoot);
|
|
610
|
-
const executable = inspectCrewxExecutable({ configPath, cwd: process.cwd() });
|
|
611
|
-
if (cleanNpxCache) {
|
|
612
|
-
cleanDoctorArtifacts(npxCache, shims);
|
|
613
|
-
npxCache = inspectNpxCache();
|
|
614
|
-
shims = inspectCrewxShims();
|
|
615
|
-
}
|
|
616
|
-
const diagnostics = [
|
|
617
|
-
checkConfig(configPath),
|
|
618
|
-
checkLogsDir(),
|
|
619
|
-
checkEnvVars(),
|
|
620
|
-
checkCrewxExecutable(executable),
|
|
621
|
-
...checkCliProviders(),
|
|
622
|
-
checkNpxCache(npxCache),
|
|
623
|
-
checkHookCommands(hooks),
|
|
624
|
-
checkCrewxShims(shims),
|
|
625
|
-
];
|
|
626
|
-
// Print diagnostics
|
|
627
|
-
diagnostics.forEach(d => {
|
|
628
|
-
const icon = statusIcon(d.status);
|
|
629
|
-
console.log(`${icon} ${d.name}: ${d.message}`);
|
|
630
|
-
if (d.details) {
|
|
631
|
-
console.log(` ${d.details}`);
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
const health = assessHealth(diagnostics);
|
|
635
|
-
const healthIcon = statusIcon(health.status);
|
|
636
|
-
console.log('');
|
|
637
|
-
console.log('─'.repeat(60));
|
|
638
|
-
console.log(`${healthIcon} Overall: ${health.message}`);
|
|
639
|
-
if (health.status === 'success') {
|
|
640
|
-
console.log('\nNext steps:');
|
|
641
|
-
console.log(' crewx q --to crewx "hello"');
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
console.log('\nAddress the issues above, then run `crewx doctor` again.');
|
|
645
|
-
}
|
|
646
|
-
if (health.status === 'error') {
|
|
647
|
-
process.exit(1);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.CLI_PROVIDER_INFO=void 0;exports.formatBytes=$;exports.inspectNpxCache=y;exports.inspectHookCommands=R;exports.inspectCrewxExecutable=E;exports.inspectCrewxShims=x;exports.checkCliProviders=S;exports.handleDoctor=ee;const w=require("node:child_process");const u=require("node:fs");const F=require("node:os");const c=require("node:path");const d=require("@crewx/sdk");const N=require("./parse-common-flags");const I=require("./hook/command-marker");exports.CLI_PROVIDER_INFO={codex:{cmd:"codex",install:"npm install -g @openai/codex"},claude:{cmd:"claude",install:"npm install -g @anthropic-ai/claude-code"},grok:{cmd:"grok",install:"curl -fsSL https://x.ai/cli/install.sh | bash"},opencode:{cmd:"opencode",install:"npm install -g opencode"},antigravity:{cmd:"antigravity",install:"npm install -g antigravity"},copilot:{cmd:"gh",install:"brew install gh # or visit cli.github.com"}};function v(e){switch(e){case"success":return"\u2705";case"warning":return"\u26A0\uFE0F ";case"error":return"\u274C"}}function P(e){try{(0,w.execSync)(`which ${e}`,{stdio:"ignore"});return true}catch{return false}}function $(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB","TB"];let n=e;let s=0;while(n>=1024&&s<t.length-1){n/=1024;s+=1}const o=s===0||n>=10?Math.round(n):n.toFixed(1);return`${o} ${t[s]}`}function p(e){try{const t=(0,u.lstatSync)(e);if(!t.isDirectory())return t.size;return(0,u.readdirSync)(e,{withFileTypes:true}).reduce((n,s)=>{return n+p((0,c.join)(e,s.name))},0)}catch{return 0}}function b(e){try{return(0,u.readdirSync)(e,{withFileTypes:true}).filter(t=>t.isDirectory()).map(t=>(0,c.join)(e,t.name)).sort()}catch{return[]}}function _(e){try{const t=JSON.parse((0,u.readFileSync)(e,"utf8"));if(typeof t!=="object"||t===null||Array.isArray(t))return void 0;return t}catch{return void 0}}function O(e=process.env){const t=e.NPM_CONFIG_CACHE??e.npm_config_cache;const n=t?.trim()||(0,c.join)((0,F.homedir)(),".npm");const s=(0,c.resolve)(n);return(0,c.basename)(s)==="_npx"?s:(0,c.join)(s,"_npx")}const L=["dependencies","devDependencies","optionalDependencies","peerDependencies"];function T(e){const t=_((0,c.join)(e,"package.json"));if(!t)return[];const n=[];for(const s of L){const o=t[s];if(typeof o!=="object"||o===null||Array.isArray(o))continue;for(const[i,r]of Object.entries(o)){if(typeof r!=="string"||!r.startsWith("file:"))continue;const a=(0,c.resolve)(e,r.slice("file:".length));if(!(0,u.existsSync)(a)){n.push(`${s}.${i}=${r} (missing ${a})`)}}}return n}function k(e,t,n){const s=e.get(t);if(s){s.details.push(...n);return s}const o={directory:t,sizeBytes:p(t),details:[...n]};e.set(t,o);return o}function y(e=O()){const t=(0,c.resolve)(e);const n=[];const s=[];const o=new Map;for(const i of b(t)){const r=(0,c.join)(i,"package-lock.json");try{const l=(0,u.statSync)(r);if(l.isFile()&&l.size===0){const h=k(o,i,[`0-byte lockfile: ${r}`]);n.push(h)}}catch{}const a=T(i);if(a.length>0){const l=k(o,i,a);s.push(l)}}return{cacheRoot:t,zeroByteLockfileDirectories:n,staleFileCacheDirectories:s,cleanableDirectories:[...o.values()]}}function C(e){if(typeof e!=="object"||e===null||Array.isArray(e))return void 0;return e}function X(e){const t=C(e.hooks)??e;const n=t.PreToolUse;if(!Array.isArray(n))return[];const s=[];for(const o of n){const i=C(o);const r=i?.hooks;if(!Array.isArray(r))continue;for(const a of r){const l=C(a)?.command;if(typeof l==="string"&&I.CREWX_HOOK_COMMAND_RE.test(l)){s.push(l)}}}return s}function B(e){const t=e.trim();if(!t)return"";if(t.startsWith("'")||t.startsWith('"')){const n=t[0];const s=t.indexOf(n,1);return s===-1?t.slice(1):t.slice(1,s)}return t.split(/\s+/,1)[0]}function H(e){return(0,c.isAbsolute)(e)||/^[A-Za-z]:[\\/]/.test(e)||e.startsWith("\\\\")}function q(e){const t=B(e);if(/^(?:npx|pnpm|yarn)$/i.test(t))return"launcher";if(!H(t))return"non-absolute";return void 0}function R(e){const t=[{provider:"claude",settingsPath:(0,c.join)((0,c.resolve)(e),".claude","settings.json")},{provider:"codex",settingsPath:(0,c.join)((0,c.resolve)(e),".codex","hooks.json")}];const n=[];const s=[];for(const o of t){if(!(0,u.existsSync)(o.settingsPath))continue;n.push(o.settingsPath);const i=_(o.settingsPath);if(!i)continue;for(const r of X(i)){const a=q(r);if(a){s.push({provider:o.provider,settingsPath:o.settingsPath,command:r,reason:a})}}}return{inspectedFiles:n,issues:s}}function W(e,t="global_first"){try{const n=(0,d.loadYamlFile)(e);const s=n.settings?.bin_resolution;return s==="local_first"||s==="global_first"?s:t}catch{return t}}function z(e,t,n){if(n!=="win32")return[];try{(0,d.primeWindowsConsoleCodePage)();const s=(0,w.execFileSync)("where.exe",["crewx"],{cwd:e,env:t,encoding:null,stdio:["ignore","pipe","ignore"]});return(0,d.decodeConsoleBuffer)(s,{platform:n}).split(/\r?\n/).map(o=>o.trim()).filter(o=>o.length>0)}catch{return[]}}function E(e={}){const t=e.platform??process.platform;const n=(0,c.resolve)(e.cwd??process.cwd());const s=e.env??process.env;const o=e.configPath??(0,c.join)(n,"crewx.yaml");const i=e.resolution??(0,d.resolveCrewxExecutable)({env:s,cwd:n,platform:t});const r=e.pathCandidates?[...e.pathCandidates]:z(n,s,t);const a=i.ok?Object.keys(i.requiredEnv??{}).filter(l=>l==="ELECTRON_RUN_AS_NODE"):[];return{platform:t,pathCandidates:r,...i.ok?{stage:i.stage,argv:[...i.argv],adoptedPath:i.argv[0]}:{resolutionError:i.reason},electron:a.includes("ELECTRON_RUN_AS_NODE"),requiredEnvKeys:a,binResolution:e.binResolution??W(o)}}function M(e){const t=[`platform: ${e.platform}`,`stage: ${e.stage??"(unresolved)"}`,`argv: ${e.argv?(0,d.formatCrewxExecutableArgv)(e.argv):"(unresolved)"}`,`adopted path: ${e.adoptedPath??"(none)"}`,`Electron runtime: ${e.electron?"yes":"no"}`,`required environment keys: ${e.requiredEnvKeys.length>0?e.requiredEnvKeys.join(", "):"(none)"}`,`bin_resolution setting: ${e.binResolution}`,...e.platform==="win32"?[`where.exe crewx candidates: ${e.pathCandidates.length>0?e.pathCandidates.join(", "):"(none)"}`]:[]];if(e.resolutionError){return{name:"CrewX Executable",status:"warning",message:"Could not resolve the CrewX executable",details:`${t.join("\n")}
|
|
2
|
+
Resolution failed; no environment values were printed.`}}return{name:"CrewX Executable",status:"success",message:"SDK child executable contract is available",details:t.join("\n")}}function x(e=(0,d.getCrewxHome)(),t=(()=>{const n=(0,d.resolveCrewxExecutable)();return n.ok?(0,d.crewxCliShimFingerprint)(n):void 0})()){const n=(0,c.join)((0,c.resolve)(e),"bin-shim");const s=[];if(t){for(const o of b(n)){const i=o.slice(n.length+1);if(i===t)continue;s.push({fingerprint:i,directory:o,sizeBytes:p(o),details:[`not current fingerprint (${t})`]})}}return{shimRoot:n,currentFingerprint:t,staleDirectories:s}}function K(e){const t=e.zeroByteLockfileDirectories.length;const n=e.staleFileCacheDirectories.length;const s=e.cleanableDirectories.length;if(t===0&&n===0){return{name:"npx Cache",status:"success",message:`No stale entries found in ${e.cacheRoot}`}}const o=[];if(t>0){o.push(`0-byte package-lock.json: ${e.zeroByteLockfileDirectories.map(i=>i.directory).join(", ")}`)}if(n>0){o.push(`stale file: cache: ${e.staleFileCacheDirectories.map(i=>i.directory).join(", ")}`)}o.push("Run `crewx doctor --clean-npx-cache` to remove these cache directories explicitly.");return{name:"npx Cache",status:"warning",message:`Found ${s} problematic npx cache director${s===1?"y":"ies"}`,details:o.join("\n")}}function U(e){if(e.issues.length===0){return{name:"CrewX Hooks",status:"success",message:e.inspectedFiles.length>0?"Installed CrewX hook commands use absolute entrypoints":"No installed CrewX hooks found"}}const t=e.issues.map(n=>`${n.settingsPath}: ${n.command} (${n.reason})`).join("\n");return{name:"CrewX Hooks",status:"warning",message:`Found ${e.issues.length} hook command(s) that need an absolute CrewX entrypoint`,details:`${t}
|
|
3
|
+
Run \`crewx hook install\` to refresh the hook after reviewing the change.`}}function V(e){if(!e.currentFingerprint){return{name:"CrewX PATH Shims",status:"warning",message:"Could not resolve the current CrewX executable; skipped orphan shim cleanup",details:`Shim root: ${e.shimRoot}`}}if(e.staleDirectories.length===0){return{name:"CrewX PATH Shims",status:"success",message:"No orphaned CrewX PATH shims found"}}return{name:"CrewX PATH Shims",status:"warning",message:`Found ${e.staleDirectories.length} orphaned CrewX PATH shim director${e.staleDirectories.length===1?"y":"ies"}`,details:`${e.staleDirectories.map(t=>t.directory).join(", ")}
|
|
4
|
+
Run \`crewx doctor --clean-npx-cache\` to remove stale shims explicitly.`}}function D(e,t){const n=(0,c.resolve)(t);let s=0;const o=[];for(const i of e){const r=(0,c.resolve)(i.directory);if((0,c.dirname)(r)!==n){o.push(`${r} (unexpected parent)`);continue}try{(0,u.rmSync)(r,{recursive:true,force:true});if(!(0,u.existsSync)(r))s+=1;else o.push(r)}catch{o.push(r)}}return{removed:s,failed:o}}function G(e,t){const n=e.cleanableDirectories;const s=t.staleDirectories;const o=[...n,...s];const i=o.reduce((h,g)=>h+g.sizeBytes,0);console.log(`\u26A0\uFE0F --clean-npx-cache: before cleanup, ${n.length} npx cache director${n.length===1?"y":"ies"} and ${s.length} orphan shim director${s.length===1?"y":"ies"} (${$(i)}).`);const r=D(n,e.cacheRoot);const a=D(s,t.shimRoot);const l=[...r.failed,...a.failed];console.log(` Removed ${r.removed+a.removed} director${r.removed+a.removed===1?"y":"ies"}.`);if(l.length>0){console.log(` Could not remove: ${l.join(", ")}`)}}function J(e){if(!(0,u.existsSync)(e)){return{name:"Configuration File",status:"error",message:`Configuration file not found: ${e}`,details:"Run 'crewx init' to create a default configuration file."}}try{const t=(0,d.loadYamlFile)(e);const n=t;if(!n?.agents||!Array.isArray(n.agents)){return{name:"Configuration File",status:"error",message:"Invalid configuration format",details:"Configuration file exists but has invalid structure."}}if(n.agents.length===0){return{name:"Configuration File",status:"warning",message:"No agents configured",details:"Add agent definitions to your crewx.yaml."}}return{name:"Configuration File",status:"success",message:`Found ${n.agents.length} agent(s) in ${e}`}}catch(t){return{name:"Configuration File",status:"error",message:"Failed to parse configuration",details:`Error: ${t instanceof Error?t.message:String(t)}`}}}function Y(){const e=(0,c.join)(process.cwd(),".crewx","logs");if(!(0,u.existsSync)(e)){return{name:"Logs Directory",status:"warning",message:"Logs directory not found",details:`Will be created automatically at ${e}`}}return{name:"Logs Directory",status:"success",message:`Logs directory exists: ${e}`}}function S(){return d.PROVIDER_ORDER.map(e=>{const t=exports.CLI_PROVIDER_INFO[e];if(!t){return{name:`${e.toUpperCase()} CLI`,status:"error",message:"Diagnostic metadata is missing",details:`Add ${e} to CLI_PROVIDER_INFO.`}}const n=e==="copilot"?"copilot (gh)":e;const s=P(t.cmd);return{name:`${n.toUpperCase()} CLI`,status:s?"success":"warning",message:s?"Installed and available":"Not installed or not on PATH",details:s?void 0:`Install: ${t.install}`}})}function Z(){const e=process.env.CREWX_CLI;if(e){return{name:"CREWX_CLI",status:"success",message:`Set to: ${e}`}}return{name:"CREWX_CLI",status:"warning",message:'Not set \u2014 defaulting to "crewx"',details:"This is auto-set at CLI startup; no action needed."}}function Q(e){const t=e.filter(o=>o.status==="error").length;const n=e.filter(o=>o.status==="warning").length;const s=e.filter(o=>o.status==="success").length;if(t>0){return{status:"error",message:`System has ${t} error(s) that need attention`}}if(n>0){return{status:"warning",message:`System is functional with ${n} warning(s)`}}return{status:"success",message:`System is healthy \u2014 all ${s} checks passed`}}async function ee(e){const t=e.includes("--clean-npx-cache");const n=e.filter(f=>f!=="--clean-npx-cache");const{config:s}=(0,N.parseCommonFlags)(n);const o=s??process.env.CREWX_CONFIG??(0,c.join)(process.cwd(),"crewx.yaml");console.log("\u{1FA7A} Starting CrewX system diagnosis...\n");let i=y();let r=x();const a=(0,c.dirname)((0,c.resolve)(o));const l=R(a);const h=E({configPath:o,cwd:process.cwd()});if(t){G(i,r);i=y();r=x()}const g=[J(o),Y(),Z(),M(h),...S(),K(i),U(l),V(r)];g.forEach(f=>{const A=v(f.status);console.log(`${A} ${f.name}: ${f.message}`);if(f.details){console.log(` ${f.details}`)}});const m=Q(g);const j=v(m.status);console.log("");console.log("\u2500".repeat(60));console.log(`${j} Overall: ${m.message}`);if(m.status==="success"){console.log("\nNext steps:");console.log(' crewx q --to crewx "hello"')}else{console.log("\nAddress the issues above, then run `crewx doctor` again.")}if(m.status==="error"){process.exit(1)}}
|