cli-jaw 2.2.13 → 2.2.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-jaw",
3
- "version": "2.2.13",
3
+ "version": "2.2.15",
4
4
  "description": "Personal AI assistant powered by Pi, Antigravity, AI-E, Claude, Claude E, Codex, Codex App, Cursor, Grok, Kiro, OpenCode, and Copilot — Web, Terminal, Telegram, and Discord interfaces with 107 built-in skills",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -82,7 +82,7 @@ const GATES = {
82
82
  ];
83
83
  const r = run('npx', args);
84
84
  if (r.status !== 0) {
85
- return { ok: false, detail: `tests failed:\n${(r.stdout || r.stderr || '').slice(-2000)}` };
85
+ return { ok: false, detail: `tests failed:\n${[r.stdout, r.stderr].filter(Boolean).join('\n').slice(-2000)}` };
86
86
  }
87
87
  return { ok: true, detail: `passed ${targets.length} suite(s): ${targets.join(', ')}` };
88
88
  },
@@ -382,7 +382,7 @@ const GATES = {
382
382
  for (const s of steps) {
383
383
  const r = run(s.cmd, s.args, { timeout: s.timeout });
384
384
  if (r.status !== 0) {
385
- return { ok: false, detail: `${s.name} failed:\n${(r.stdout || r.stderr || '').slice(-1500)}` };
385
+ return { ok: false, detail: `${s.name} failed:\n${[r.stdout, r.stderr].filter(Boolean).join('\n').slice(-1500)}` };
386
386
  }
387
387
  }
388
388
  return { ok: true, detail: 'docs:check + check-doc-drift.sh clean' };
@@ -393,7 +393,7 @@ const GATES = {
393
393
  check() {
394
394
  const r = run('node', ['scripts/check-strict-baseline.mjs'], { timeout: 120_000 });
395
395
  if (r.status !== 0) {
396
- return { ok: false, detail: `strict baseline regressed:\n${(r.stdout || r.stderr || '').slice(-1200)}` };
396
+ return { ok: false, detail: `strict baseline regressed:\n${[r.stdout, r.stderr].filter(Boolean).join('\n').slice(-1200)}` };
397
397
  }
398
398
  return { ok: true, detail: 'live any/debt/allow counts within frozen baseline' };
399
399
  },
@@ -403,7 +403,7 @@ const GATES = {
403
403
  check() {
404
404
  const r = run('node', ['scripts/check-redaction-sinks.mjs'], { timeout: 60_000 });
405
405
  if (r.status !== 0) {
406
- return { ok: false, detail: `unmasked channel sink:\n${(r.stdout || r.stderr || '').slice(-1500)}` };
406
+ return { ok: false, detail: `unmasked channel sink:\n${[r.stdout, r.stderr].filter(Boolean).join('\n').slice(-1500)}` };
407
407
  }
408
408
  return { ok: true, detail: 'no unmasked sink in telegram/discord/slack/telegram-hub' };
409
409
  },
@@ -413,7 +413,7 @@ const GATES = {
413
413
  check() {
414
414
  const r = run('node', ['scripts/sync-electron-version.cjs', '--check'], { timeout: 60_000 });
415
415
  if (r.status !== 0) {
416
- return { ok: false, detail: (r.stdout || r.stderr || '').trim().slice(-500) };
416
+ return { ok: false, detail: [r.stdout, r.stderr].filter(Boolean).join('\n').trim().slice(-500) };
417
417
  }
418
418
  return { ok: true, detail: 'desktop artifacts will carry the release version' };
419
419
  },
@@ -423,7 +423,7 @@ const GATES = {
423
423
  check() {
424
424
  const r = run('node', ['scripts/check-sidecar-prune-safety.mjs'], { timeout: 60_000 });
425
425
  if (r.status !== 0) {
426
- return { ok: false, detail: (r.stdout || r.stderr || '').trim().slice(-800) };
426
+ return { ok: false, detail: [r.stdout, r.stderr].filter(Boolean).join('\n').trim().slice(-800) };
427
427
  }
428
428
  return { ok: true, detail: 'packaged sidecar keeps every runtime dependency' };
429
429
  },
@@ -439,7 +439,12 @@ const GATES = {
439
439
  return { ok: true, detail: 'SKIPPED — electron/node_modules absent, nothing probed' };
440
440
  }
441
441
  if (r.status !== 0) {
442
- return { ok: false, detail: (r.stdout || r.stderr || '').trim().slice(-800) };
442
+ // The probe prints its notes on stdout and its reasons on
443
+ // stderr. `stdout || stderr` therefore threw the reasons away
444
+ // whenever a single note had been printed, which is how a Linux
445
+ // failure reached CI showing only "prebuilds present" and
446
+ // "round-trip ok" with no stated cause.
447
+ return { ok: false, detail: [r.stdout, r.stderr].filter(Boolean).join('\n').trim().slice(-800) };
443
448
  }
444
449
  return { ok: true, detail: 'node-pty loads under this runtime' };
445
450
  },
@@ -452,7 +457,7 @@ const GATES = {
452
457
  return { ok: true, detail: 'SKIPPED — sidecar not bundled, nothing imported' };
453
458
  }
454
459
  if (r.status !== 0) {
455
- return { ok: false, detail: (r.stdout || r.stderr || '').trim().slice(-800) };
460
+ return { ok: false, detail: [r.stdout, r.stderr].filter(Boolean).join('\n').trim().slice(-800) };
456
461
  }
457
462
  return { ok: true, detail: 'bundled sidecar imports its critical entry surfaces' };
458
463
  },
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Run electron-builder with a node-gyp-capable python resolved first.
4
+ *
5
+ * The scripts used to inline the lookup as
6
+ * `PYTHON="$(bash ../scripts/pick-gyp-python.sh)" electron-builder --win`.
7
+ * npm hands package scripts to cmd.exe on Windows, which does not expand
8
+ * `$(...)`, so the literal text arrived as a filename and the Windows job of
9
+ * every desktop release from v2.2.11 onward died before electron-builder even
10
+ * started:
11
+ *
12
+ * PYTHON: can't open file 'D:\a\cli-jaw\cli-jaw\electron\=$(bash ..\scripts\pick-gyp-python.sh)'
13
+ *
14
+ * macOS and Linux kept building, so the release looked successful while its
15
+ * Windows artifacts silently went missing.
16
+ *
17
+ * Node is the one interpreter guaranteed to exist wherever npm runs a script,
18
+ * so the resolution happens here instead of in shell syntax. The picker itself
19
+ * stays in bash and stays authoritative; it is simply invoked rather than
20
+ * interpolated. When bash is unavailable (a plain Windows box without Git
21
+ * Bash), the build proceeds with the ambient python3: node-gyp then fails with
22
+ * its own diagnostic instead of a confusing missing-file error.
23
+ */
24
+ import { spawnSync } from 'node:child_process';
25
+ import { dirname, join } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+
28
+ const scriptsDir = dirname(fileURLToPath(import.meta.url));
29
+ const picker = join(scriptsDir, 'pick-gyp-python.sh');
30
+
31
+ /** Ask the bash picker for an interpreter that still has distutils. */
32
+ function resolvePython() {
33
+ if (process.env['PYTHON']) return process.env['PYTHON'];
34
+ const probe = spawnSync('bash', [picker], { encoding: 'utf8' });
35
+ if (probe.status !== 0) {
36
+ const reason = probe.error ? probe.error.message : (probe.stderr || '').trim();
37
+ console.warn(`[gyp-python] picker unavailable (${reason || 'unknown'}); leaving python resolution to node-gyp`);
38
+ return null;
39
+ }
40
+ return probe.stdout.trim() || null;
41
+ }
42
+
43
+ const python = resolvePython();
44
+ const env = { ...process.env };
45
+ if (python) {
46
+ env['PYTHON'] = python;
47
+ // node-gyp does not read PYTHON on its own; npm_config_python is the knob
48
+ // it actually consults.
49
+ env['npm_config_python'] = python;
50
+ }
51
+
52
+ const args = process.argv.slice(2);
53
+ const builder = process.platform === 'win32' ? 'electron-builder.cmd' : 'electron-builder';
54
+ const result = spawnSync(builder, args, { stdio: 'inherit', env, shell: process.platform === 'win32' });
55
+
56
+ if (result.error) {
57
+ console.error(`[electron-builder] failed to start: ${result.error.message}`);
58
+ process.exit(1);
59
+ }
60
+ process.exit(result.status ?? 1);