@mondaydotcomorg/z2h-cli 0.13.2 → 0.14.0

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=doctor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/doctor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface DoctorOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function doctorCommand(opts?: DoctorOptions): Promise<void>;
5
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAiSD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyE3E"}
@@ -0,0 +1,334 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const node_module = require('node:module');
4
+ const promises = require('node:fs/promises');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+ const execa = require('execa');
8
+ const semver = require('semver');
9
+ const fs = require('fs-extra');
10
+ const pc = require('picocolors');
11
+ const util_logger = require('../util/logger.js');
12
+ const util_workspace = require('../util/workspace.js');
13
+ const util_auth_authStore = require('../util/auth/auth-store.js');
14
+ const constants = require('../constants.js');
15
+
16
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
17
+ const _interopDefault = e => e && e.__esModule ? e : { default: e };
18
+
19
+ const os__default = /*#__PURE__*/_interopDefault(os);
20
+ const path__default = /*#__PURE__*/_interopDefault(path);
21
+ const semver__default = /*#__PURE__*/_interopDefault(semver);
22
+ const pc__default = /*#__PURE__*/_interopDefault(pc);
23
+
24
+ const HOME = os__default.default.homedir();
25
+ const Z2H_REPO_ROOT = path__default.default.join(HOME, 'Development', 'bigbrain-z2h');
26
+ const BOOTSTRAP_CMD = `bash ${path__default.default.join(Z2H_REPO_ROOT, 'z2h/setup/bootstrap.sh')}`;
27
+ const UPDATE_CMD = `bash ${path__default.default.join(Z2H_REPO_ROOT, 'z2h/setup/update.sh')}`;
28
+ const CLAUDE_PLUGINS_JSON = path__default.default.join(HOME, '.claude', 'plugins', 'installed_plugins.json');
29
+ const SSL_CERT_PATH = path__default.default.join(HOME, 'Library', 'Application Support', 'devcert', 'domains', 'webpack.llama.fan', 'certificate.crt');
30
+ // ── helpers ───────────────────────────────────────────────────────────────────
31
+ async function checkTool(label, opts) {
32
+ const { cmd, args, failStatus = 'fail', getVersion, fix } = opts;
33
+ let stdout;
34
+ try {
35
+ ({ stdout } = await execa.execa(cmd, args));
36
+ stdout = stdout.trim();
37
+ }
38
+ catch {
39
+ return { label, status: failStatus, detail: 'not found', fix };
40
+ }
41
+ let version;
42
+ try {
43
+ version = getVersion ? await getVersion(stdout) : stdout;
44
+ }
45
+ catch {
46
+ /* best-effort */
47
+ }
48
+ return { label, status: 'ok', detail: version };
49
+ }
50
+ // ── checks ───────────────────────────────────────────────────────────────────
51
+ function checkNvm() {
52
+ const nvmScript = path__default.default.join(HOME, '.nvm', 'nvm.sh');
53
+ return checkTool('nvm', {
54
+ cmd: 'bash',
55
+ args: ['-c', `source "${nvmScript}" && nvm --version`],
56
+ failStatus: 'warn',
57
+ fix: BOOTSTRAP_CMD,
58
+ });
59
+ }
60
+ async function checkNode() {
61
+ try {
62
+ const { stdout } = await execa.execa('node', ['--version']);
63
+ const current = stdout.trim();
64
+ let requiredNodeVersion;
65
+ try {
66
+ const nvmrc = (await promises.readFile(path__default.default.join(Z2H_REPO_ROOT, '.nvmrc'), 'utf8')).trim();
67
+ requiredNodeVersion = semver__default.default.coerce(nvmrc)?.version;
68
+ }
69
+ catch {
70
+ // repo not cloned — skip version comparison
71
+ }
72
+ if (!requiredNodeVersion) {
73
+ return { label: 'node', status: 'ok', detail: current };
74
+ }
75
+ const matches = semver__default.default.satisfies(current, `~${requiredNodeVersion}`);
76
+ return {
77
+ label: 'node',
78
+ status: matches ? 'ok' : 'fail',
79
+ detail: matches ? current : `${current} (expected ${requiredNodeVersion})`,
80
+ fix: matches ? undefined : BOOTSTRAP_CMD,
81
+ };
82
+ }
83
+ catch {
84
+ return { label: 'node', status: 'fail', detail: 'not found', fix: BOOTSTRAP_CMD };
85
+ }
86
+ }
87
+ function checkYarn() {
88
+ return checkTool('yarn', { cmd: 'yarn', args: ['--version'], fix: BOOTSTRAP_CMD });
89
+ }
90
+ function checkAws() {
91
+ return checkTool('aws', { cmd: 'aws', args: ['--version'], getVersion: s => s.split(/\s/)[0], fix: BOOTSTRAP_CMD });
92
+ }
93
+ function checkGitRemoteS3() {
94
+ return checkTool('git-remote-s3', {
95
+ cmd: 'which',
96
+ args: ['git-remote-s3'],
97
+ getVersion: async () => {
98
+ const { stdout: pipxOut } = await execa.execa('pipx', ['runpip', 'git-remote-s3', 'show', 'git-remote-s3']);
99
+ return pipxOut.match(/^Version:\s+(.+)/m)?.[1]?.trim();
100
+ },
101
+ fix: BOOTSTRAP_CMD,
102
+ });
103
+ }
104
+ async function checkZ2hCli() {
105
+ const { version } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('commands/doctor.js', document.baseURI).href)))('../../package.json');
106
+ let latestVersion;
107
+ try {
108
+ const token = util_auth_authStore.tryReadAuthTokenSync();
109
+ if (token) {
110
+ const res = await fetch(`${constants.brokerNpmRegistryUrl()}/@mondaydotcomorg%2Fz2h-cli`, {
111
+ headers: { Authorization: `Bearer ${token}`, ...constants.routingHeaders() },
112
+ signal: AbortSignal.timeout(5000),
113
+ });
114
+ if (res.ok) {
115
+ const data = (await res.json());
116
+ latestVersion = data['dist-tags']?.latest;
117
+ }
118
+ }
119
+ }
120
+ catch {
121
+ // version check is best-effort — no network / token not ready
122
+ }
123
+ if (latestVersion && latestVersion !== version) {
124
+ return { label: 'z2h-cli', status: 'warn', detail: `${version} (latest: ${latestVersion})`, fix: UPDATE_CMD };
125
+ }
126
+ return { label: 'z2h-cli', status: 'ok', detail: latestVersion ? `${version} (up to date)` : version };
127
+ }
128
+ async function checkAuthToken() {
129
+ try {
130
+ const parsed = (await fs.readJson(util_auth_authStore.Z2H_AUTH_FILE));
131
+ if (!parsed.token) {
132
+ return {
133
+ label: 'auth token',
134
+ status: 'fail',
135
+ detail: `${util_auth_authStore.Z2H_AUTH_FILE} missing token field`,
136
+ fix: 'z2h-cli generate-z2h-token',
137
+ };
138
+ }
139
+ return { label: 'auth token', status: 'ok', detail: util_auth_authStore.Z2H_AUTH_FILE };
140
+ }
141
+ catch (err) {
142
+ let detail;
143
+ if (err.code === 'ENOENT') {
144
+ detail = `${util_auth_authStore.Z2H_AUTH_FILE} not found`;
145
+ }
146
+ else if (err instanceof SyntaxError) {
147
+ detail = `${util_auth_authStore.Z2H_AUTH_FILE} could not be parsed`;
148
+ }
149
+ else {
150
+ detail = `${util_auth_authStore.Z2H_AUTH_FILE} is unreadable`;
151
+ }
152
+ return { label: 'auth token', status: 'fail', detail, fix: 'z2h-cli generate-z2h-token' };
153
+ }
154
+ }
155
+ async function checkNpmTokenInProfile() {
156
+ const profiles = [
157
+ path__default.default.join(HOME, 'dotfiles', '.bash_profile'),
158
+ path__default.default.join(HOME, '.zshrc'),
159
+ path__default.default.join(HOME, '.bash_profile'),
160
+ ];
161
+ for (const profile of profiles) {
162
+ if (!(await fs.pathExists(profile))) {
163
+ continue;
164
+ }
165
+ try {
166
+ const content = await promises.readFile(profile, 'utf8');
167
+ if (content.includes('export Z2H_NPM_TOKEN')) {
168
+ return { label: 'npm token in profile', status: 'ok', detail: path__default.default.basename(profile) };
169
+ }
170
+ }
171
+ catch {
172
+ // skip unreadable profile
173
+ }
174
+ }
175
+ return {
176
+ label: 'npm token in profile',
177
+ status: 'fail',
178
+ detail: 'export Z2H_NPM_TOKEN not found in any shell profile',
179
+ fix: BOOTSTRAP_CMD,
180
+ };
181
+ }
182
+ async function checkWorkspace() {
183
+ const home = util_workspace.getWorkspaceHome();
184
+ const ok = await util_workspace.isZ2hWorkspace(home);
185
+ return {
186
+ label: 'workspace',
187
+ status: ok ? 'ok' : 'fail',
188
+ detail: ok ? home : `${home} not initialized`,
189
+ fix: ok ? undefined : 'z2h-cli create-workspace',
190
+ };
191
+ }
192
+ async function checkReactVersion() {
193
+ const REACT_VERSION_LABEL = 'react version';
194
+ const REQUIRED_REACT_VERSION = '18.x';
195
+ const reactVersionFail = detail => ({
196
+ label: REACT_VERSION_LABEL,
197
+ status: 'fail',
198
+ detail,
199
+ fix: BOOTSTRAP_CMD,
200
+ });
201
+ const home = util_workspace.getWorkspaceHome();
202
+ let pkg;
203
+ try {
204
+ pkg = (await fs.readJson(path__default.default.join(home, 'package.json')));
205
+ }
206
+ catch {
207
+ return reactVersionFail('could not read workspace package.json');
208
+ }
209
+ const reactVersion = pkg.dependencies?.react;
210
+ if (!reactVersion) {
211
+ return reactVersionFail('react not in workspace dependencies');
212
+ }
213
+ const coerced = semver__default.default.coerce(reactVersion)?.version;
214
+ if (!coerced || !semver__default.default.satisfies(coerced, REQUIRED_REACT_VERSION)) {
215
+ return reactVersionFail(`invalid react version: ${reactVersion} (expected ${REQUIRED_REACT_VERSION})`);
216
+ }
217
+ return {
218
+ label: REACT_VERSION_LABEL,
219
+ status: 'ok',
220
+ detail: reactVersion,
221
+ };
222
+ }
223
+ async function checkSslCert() {
224
+ const ok = await fs.pathExists(SSL_CERT_PATH);
225
+ return {
226
+ label: 'SSL cert',
227
+ status: ok ? 'ok' : 'warn',
228
+ detail: ok ? 'webpack.llama.fan' : 'not found — auto-installs on first z2h-cli dev',
229
+ fix: ok ? undefined : 'z2h-cli dev',
230
+ };
231
+ }
232
+ async function checkPlugin(pluginKey, label, isMandatory, pluginsJson) {
233
+ const missingStatus = isMandatory ? 'fail' : 'warn';
234
+ if (!pluginsJson) {
235
+ return { label, status: missingStatus, detail: `${CLAUDE_PLUGINS_JSON} not found`, fix: BOOTSTRAP_CMD };
236
+ }
237
+ const entries = pluginsJson.plugins?.[pluginKey];
238
+ if (!entries?.length) {
239
+ return { label, status: missingStatus, detail: `plugin ${pluginKey} not registered`, fix: BOOTSTRAP_CMD };
240
+ }
241
+ const installPath = entries[0].installPath;
242
+ if (!installPath) {
243
+ return { label, status: missingStatus, detail: 'installPath missing', fix: BOOTSTRAP_CMD };
244
+ }
245
+ const exists = await fs.pathExists(installPath);
246
+ return {
247
+ label,
248
+ status: exists ? 'ok' : missingStatus,
249
+ detail: exists ? installPath : `${installPath} not found`,
250
+ fix: exists ? undefined : BOOTSTRAP_CMD,
251
+ };
252
+ }
253
+ // ── rendering ─────────────────────────────────────────────────────────────────
254
+ const STATUS_ICON = {
255
+ ok: pc__default.default.green('✅'),
256
+ warn: pc__default.default.yellow('⚠️ '),
257
+ fail: pc__default.default.red('❌'),
258
+ };
259
+ function renderCheck(c) {
260
+ const icon = STATUS_ICON[c.status];
261
+ const label = c.label.padEnd(22);
262
+ const detail = c.detail ? pc__default.default.dim(c.detail) : '';
263
+ process.stdout.write(` ${icon} ${label}${detail}\n`);
264
+ }
265
+ // ── command ───────────────────────────────────────────────────────────────────
266
+ async function doctorCommand(opts = {}) {
267
+ if (opts.json) {
268
+ util_logger.setOutputMode('json');
269
+ }
270
+ let pluginsJson = null;
271
+ try {
272
+ if (await fs.pathExists(CLAUDE_PLUGINS_JSON)) {
273
+ pluginsJson = (await fs.readJson(CLAUDE_PLUGINS_JSON));
274
+ }
275
+ }
276
+ catch {
277
+ // treat as missing
278
+ }
279
+ const systemChecks = await Promise.all([checkNvm(), checkNode(), checkYarn(), checkAws(), checkGitRemoteS3()]);
280
+ const cliChecks = await Promise.all([checkZ2hCli(), checkAuthToken(), checkNpmTokenInProfile()]);
281
+ const workspaceChecks = await Promise.all([checkWorkspace(), checkReactVersion(), checkSslCert()]);
282
+ const pluginChecks = await Promise.all([
283
+ checkPlugin('z2h@bigbrain-z2h', 'z2h plugin', true, pluginsJson),
284
+ checkPlugin('cf-external-claude-plugin@client-foundations-ai-tools', 'cf-external plugin', false, pluginsJson),
285
+ checkPlugin('pr-guardrails@agentic-builders-hub', 'pr-guardrails plugin', false, pluginsJson),
286
+ ]);
287
+ const sections = [
288
+ { title: 'System tools', checks: systemChecks },
289
+ { title: 'CLI & auth', checks: cliChecks },
290
+ { title: 'Workspace', checks: workspaceChecks },
291
+ { title: 'Plugins', checks: pluginChecks },
292
+ ];
293
+ const allChecks = sections.flatMap(s => s.checks);
294
+ const issues = allChecks.filter(c => c.status !== 'ok');
295
+ if (opts.json) {
296
+ util_logger.output('', {
297
+ ok: issues.filter(c => c.status === 'fail').length === 0,
298
+ checks: allChecks,
299
+ });
300
+ return;
301
+ }
302
+ for (const section of sections) {
303
+ process.stdout.write(`\n${pc__default.default.bold(section.title)}\n`);
304
+ for (const check of section.checks) {
305
+ renderCheck(check);
306
+ }
307
+ }
308
+ process.stdout.write('\n');
309
+ if (issues.length === 0) {
310
+ process.stdout.write(pc__default.default.bold(pc__default.default.green('No issues detected 🚀\n')));
311
+ }
312
+ else {
313
+ const fails = issues.filter(c => c.status === 'fail').length;
314
+ const warns = issues.filter(c => c.status === 'warn').length;
315
+ const parts = [
316
+ fails && `${fails} failure${fails > 1 ? 's' : ''}`,
317
+ warns && `${warns} warning${warns > 1 ? 's' : ''}`,
318
+ ].filter(Boolean);
319
+ process.stdout.write(pc__default.default.yellow(`${parts.join(', ')} found\n`));
320
+ // Deduplicated fixes in the order they were first encountered
321
+ const fixes = [...new Set(issues.map(c => c.fix).filter(Boolean))];
322
+ if (fixes.length > 0) {
323
+ process.stdout.write('\n');
324
+ process.stdout.write(pc__default.default.bold(pc__default.default.red('In order to fix these issues, run the following commands:')));
325
+ process.stdout.write('\n');
326
+ for (const fix of fixes) {
327
+ process.stdout.write(` ${pc__default.default.cyan(fix)}\n`);
328
+ }
329
+ }
330
+ process.stdout.write('\n');
331
+ }
332
+ }
333
+
334
+ exports.doctorCommand = doctorCommand;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=doctor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/__tests__/doctor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface DoctorOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function doctorCommand(opts?: DoctorOptions): Promise<void>;
5
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAiSD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyE3E"}