swytchcode 2.14.0-beta.1 → 2.14.0-beta.2

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.
Files changed (2) hide show
  1. package/bin/swytchcode.js +130 -86
  2. package/package.json +7 -7
package/bin/swytchcode.js CHANGED
@@ -165,11 +165,11 @@ function spinner(msg) {
165
165
  return {
166
166
  stop(doneMsg) {
167
167
  clearInterval(iv);
168
- process.stdout.write(`\r${c.green}›${c.reset} ${doneMsg}\n`);
168
+ process.stdout.write(`\r\x1b[K${c.green}›${c.reset} ${doneMsg}\n`);
169
169
  },
170
170
  fail(failMsg) {
171
171
  clearInterval(iv);
172
- process.stdout.write(`\r${c.red}×${c.reset} ${failMsg}\n`);
172
+ process.stdout.write(`\r\x1b[K${c.red}×${c.reset} ${failMsg}\n`);
173
173
  },
174
174
  };
175
175
  }
@@ -242,9 +242,16 @@ function exitCode(result) {
242
242
  return 1;
243
243
  }
244
244
 
245
- // Explicit override: SWYTCHCODE_BIN=/path/to/binary bypasses platform resolution.
245
+ // Explicit override: SWYTCHCODE_BIN=/path/to/binary bypasses platform resolution
246
+ // and passes straight through to that binary. The one exception is a bare
247
+ // npx-style launch (npm_command=exec / npx, with no args): there we keep the
248
+ // override binary but skip the passthrough so the onboarding still runs (using
249
+ // that binary for its subprocess calls), which makes onboarding testable locally.
246
250
  const explicitBin = (process.env.SWYTCHCODE_BIN || '').trim();
247
- if (explicitBin) {
251
+ const bareNpxLaunch =
252
+ process.argv.length <= 2 &&
253
+ ((process.env.npm_command || '') === 'exec' || (process.env.npm_execpath || '').includes('npx'));
254
+ if (explicitBin && !bareNpxLaunch) {
248
255
  const result = spawnBin(explicitBin, process.argv.slice(2));
249
256
  if (result.error) {
250
257
  err(`swytchcode: failed to launch SWYTCHCODE_BIN="${explicitBin}": ${result.error.message}`);
@@ -253,40 +260,38 @@ if (explicitBin) {
253
260
  process.exit(exitCode(result));
254
261
  }
255
262
 
256
- const PLATFORM_PACKAGES = {
257
- 'darwin-arm64': 'swytchcode-cli-darwin-arm64',
258
- 'darwin-x64': 'swytchcode-cli-darwin-x64',
259
- 'linux-arm64': 'swytchcode-cli-linux-arm64',
260
- 'linux-x64': 'swytchcode-cli-linux-x64',
261
- 'win32-arm64': 'swytchcode-cli-win32-arm64',
262
- 'win32-x64': 'swytchcode-cli-win32-x64',
263
- };
264
-
265
- const platform = process.platform;
266
- const arch = process.arch;
267
- const key = `${platform}-${arch}`;
268
- const pkg = PLATFORM_PACKAGES[key];
269
-
270
- if (!pkg) {
271
- err(
272
- `swytchcode: unsupported platform "${key}".\n` +
273
- `Supported: ${Object.keys(PLATFORM_PACKAGES).join(', ')}\n` +
274
- `Install via curl instead: https://cli.swytchcode.com`
275
- );
276
- process.exit(1);
277
- }
278
-
279
- const isWindows = platform === 'win32';
280
- const binName = isWindows ? 'swytchcode.exe' : 'swytchcode';
281
263
  let binPath;
282
- try {
283
- binPath = require.resolve(`${pkg}/bin/${binName}`);
284
- } catch (_) {
285
- err(
286
- `swytchcode: could not find the binary for "${key}".\n` +
287
- `Try reinstalling: npm install -g swytchcode`
288
- );
289
- process.exit(1);
264
+ if (explicitBin) {
265
+ binPath = explicitBin;
266
+ } else {
267
+ const PLATFORM_PACKAGES = {
268
+ 'darwin-arm64': 'swytchcode-cli-darwin-arm64',
269
+ 'darwin-x64': 'swytchcode-cli-darwin-x64',
270
+ 'linux-arm64': 'swytchcode-cli-linux-arm64',
271
+ 'linux-x64': 'swytchcode-cli-linux-x64',
272
+ 'win32-arm64': 'swytchcode-cli-win32-arm64',
273
+ 'win32-x64': 'swytchcode-cli-win32-x64',
274
+ };
275
+ const key = `${process.platform}-${process.arch}`;
276
+ const pkg = PLATFORM_PACKAGES[key];
277
+ if (!pkg) {
278
+ err(
279
+ `swytchcode: unsupported platform "${key}".\n` +
280
+ `Supported: ${Object.keys(PLATFORM_PACKAGES).join(', ')}\n` +
281
+ `Install via curl instead: https://cli.swytchcode.com`
282
+ );
283
+ process.exit(1);
284
+ }
285
+ const binName = process.platform === 'win32' ? 'swytchcode.exe' : 'swytchcode';
286
+ try {
287
+ binPath = require.resolve(`${pkg}/bin/${binName}`);
288
+ } catch (_) {
289
+ err(
290
+ `swytchcode: could not find the binary for "${key}".\n` +
291
+ `Try reinstalling: npm install -g swytchcode`
292
+ );
293
+ process.exit(1);
294
+ }
290
295
  }
291
296
 
292
297
  // Detect npx
@@ -351,6 +356,39 @@ function apiGet(urlStr) {
351
356
  });
352
357
  }
353
358
 
359
+ // ─── Welcome card data (mirrors internal/output/banner.go). Declared before the
360
+ // dispatch below because runOnboarding() runs at module-load time and must not
361
+ // hit a temporal-dead-zone on these consts. ─────────────────────────────────
362
+ const CARD_WORDMARK = [
363
+ '███████╗██╗ ██╗██╗ ██╗████████╗ ██████╗██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗',
364
+ '██╔════╝██║ ██║╚██╗ ██╔╝╚══██╔══╝██╔════╝██║ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝',
365
+ '███████╗██║ █╗ ██║ ╚████╔╝ ██║ ██║ ███████║██║ ██║ ██║██║ ██║█████╗',
366
+ '╚════██║██║███╗██║ ╚██╔╝ ██║ ██║ ██╔══██║██║ ██║ ██║██║ ██║██╔══╝',
367
+ '███████║╚███╔███╔╝ ██║ ██║ ╚██████╗██║ ██║╚██████╗╚██████╔╝██████╔╝███████╗',
368
+ '╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝',
369
+ ];
370
+ const CARD_MARK = [
371
+ ' ▄▄▄█████████▄▄',
372
+ ' ▄▄███▀▀▀ ▀▀▀███▄',
373
+ ' ▄███▀',
374
+ ' ███ ▄████████▀',
375
+ ' ██▀ █████████',
376
+ ' ██▀ ▄████████▀',
377
+ '███ ▄████████▀',
378
+ '██ █████████',
379
+ '██ ▄██████████████▀',
380
+ '██ ▄██████████████▀',
381
+ '▀██ ██████▀▄█████▀',
382
+ ' ██▄ ▄████▀',
383
+ ' ██▄ ▄████▀',
384
+ ' ▀██▄ ▄██▀ ▄',
385
+ ' ▀▀▀ ▄█▀▀ ▄▄███',
386
+ ' ▄▀ ▄▄▄▄▄▄▄▄▄████▀▀',
387
+ ' ▀ ▀▀████████▀▀',
388
+ ];
389
+ const CARD_TAGLINE = 'Your agent calls an API. We make sure it goes through.';
390
+ const CARD_DESCRIPTION = 'Swytchcode sits between your agent and any API — auth, retries, idempotency, and schema validation, handled. Run a command below, or swytchcode --help for the full list.';
391
+
354
392
  // ─── Normal pass-through (not npx, or npx with non-demo args) ───────────────
355
393
  if (!isNpx || (userArgs.length > 0 && !isCanonicalDemoArg && !isNpxBareWordArg)) {
356
394
  const result = spawnBin(binPath, userArgs);
@@ -427,35 +465,7 @@ async function runDemoSelection() {
427
465
  }
428
466
  }
429
467
 
430
- // ─── Welcome card (mirrors internal/output/banner.go) ───────────────────────
431
- const CARD_WORDMARK = [
432
- '███████╗██╗ ██╗██╗ ██╗████████╗ ██████╗██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗',
433
- '██╔════╝██║ ██║╚██╗ ██╔╝╚══██╔══╝██╔════╝██║ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝',
434
- '███████╗██║ █╗ ██║ ╚████╔╝ ██║ ██║ ███████║██║ ██║ ██║██║ ██║█████╗',
435
- '╚════██║██║███╗██║ ╚██╔╝ ██║ ██║ ██╔══██║██║ ██║ ██║██║ ██║██╔══╝',
436
- '███████║╚███╔███╔╝ ██║ ██║ ╚██████╗██║ ██║╚██████╗╚██████╔╝██████╔╝███████╗',
437
- '╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝',
438
- ];
439
- const CARD_MARK = [
440
- ' ▄▄▄█████████▄▄',
441
- ' ▄▄███▀▀▀ ▀▀▀███▄',
442
- ' ▄███▀',
443
- ' ███ ▄████████▀',
444
- ' ██▀ █████████',
445
- ' ██▀ ▄████████▀',
446
- '███ ▄████████▀',
447
- '██ █████████',
448
- '██ ▄██████████████▀',
449
- '██ ▄██████████████▀',
450
- '▀██ ██████▀▄█████▀',
451
- ' ██▄ ▄████▀',
452
- ' ██▄ ▄████▀',
453
- ' ▀██▄ ▄██▀ ▄',
454
- ' ▀▀▀ ▄█▀▀ ▄▄███',
455
- ' ▄▀ ▄▄▄▄▄▄▄▄▄████▀▀',
456
- ' ▀ ▀▀████████▀▀',
457
- ];
458
- const CARD_TAGLINE = 'Your agent calls an API. We make sure it goes through.';
468
+ // ─── Welcome card render helpers (data consts are hoisted above the dispatch) ─
459
469
 
460
470
  function rep(ch, n) { return n > 0 ? ch.repeat(n) : ''; }
461
471
  function vlen(s) { return [...s].length; }
@@ -463,6 +473,27 @@ function padRows(rows) {
463
473
  const w = Math.max(...rows.map(vlen));
464
474
  return { rows: rows.map(r => r + rep(' ', w - vlen(r))), w };
465
475
  }
476
+ // wrapText greedily word-wraps s to the given visible width (in code points).
477
+ function wrapText(s, width) {
478
+ if (width < 1) width = 1;
479
+ const lines = [];
480
+ let cur = '';
481
+ for (const word of s.split(/\s+/).filter(Boolean)) {
482
+ if (cur === '') cur = word;
483
+ else if (vlen(cur) + 1 + vlen(word) <= width) cur += ' ' + word;
484
+ else { lines.push(cur); cur = word; }
485
+ }
486
+ if (cur !== '') lines.push(cur);
487
+ return lines;
488
+ }
489
+ // writeDescRows renders the wrapped description inside a framed box (boxRow closes
490
+ // over innerW), with the "swytchcode --help" hint highlighted.
491
+ function writeDescRows(boxRow, orange, contentW, margin) {
492
+ for (const ln of wrapText(CARD_DESCRIPTION, contentW)) {
493
+ const colored = ln.replace('swytchcode --help', orange('swytchcode --help'));
494
+ writeln(boxRow(rep(' ', margin) + colored, margin + vlen(ln)));
495
+ }
496
+ }
466
497
 
467
498
  // welcomeCard renders the framed welcome card (logo mark left + wordmark right,
468
499
  // wordmark vertically centered), stepping down to a plain wordmark or the compact
@@ -489,9 +520,11 @@ function welcomeCard(cliVersion) {
489
520
  const innerW = margin + mkW + gutter + wmW + margin;
490
521
  const cardW = innerW + 2;
491
522
 
523
+ const wmCardW = margin + wmW + margin + 2; // framed wordmark-only box width
524
+
492
525
  if (!isTTY || noColor) { compactCard(cliVersion, false); return; }
493
- if (!utf8 || width < 87) { compactCard(cliVersion, useColor); return; }
494
- if (!truecolor || width < cardW + 2) { wordmarkCard({ orange, dim, green, wm }); return; }
526
+ if (!utf8 || width < wmCardW + 2) { compactCard(cliVersion, useColor); return; }
527
+ if (!truecolor || width < cardW + 2) { wordmarkCard({ orange, dim, frame, green, wm, wmW }); return; }
495
528
 
496
529
  const boxRow = (colored, vis) => frame('│') + colored + rep(' ', Math.max(0, innerW - vis)) + frame('│');
497
530
  writeln();
@@ -512,6 +545,8 @@ function welcomeCard(cliVersion) {
512
545
  const lead = margin + Math.floor((innerW - 2 * margin - vlen(CARD_TAGLINE)) / 2);
513
546
  writeln(boxRow(rep(' ', lead) + dim(CARD_TAGLINE), lead + vlen(CARD_TAGLINE)));
514
547
  writeln(boxRow('', 0));
548
+ writeDescRows(boxRow, orange, innerW - 2 * margin, margin);
549
+ writeln(boxRow('', 0));
515
550
  writeln(frame('└' + rep('─', innerW) + '┘'));
516
551
  cardBelow({ orange, dim, green });
517
552
  }
@@ -528,18 +563,27 @@ function compactCard(cliVersion, useColor) {
528
563
  writeln();
529
564
  }
530
565
 
531
- function wordmarkCard({ orange, dim, green, wm }) {
566
+ function wordmarkCard({ orange, dim, frame, green, wm, wmW }) {
567
+ const margin = 2;
568
+ const innerW = margin + wmW + margin;
569
+ const boxRow = (colored, vis) => frame('│') + colored + rep(' ', Math.max(0, innerW - vis)) + frame('│');
532
570
  writeln();
533
- for (const row of wm) writeln(` ${orange(row)}`);
534
- writeln(`\n ${dim(CARD_TAGLINE)}`);
571
+ const title = ' Welcome to Swytchcode ';
572
+ writeln(frame('┌─' + title + rep('─', innerW - 1 - vlen(title)) + '┐'));
573
+ writeln(boxRow('', 0));
574
+ for (const row of wm) writeln(boxRow(rep(' ', margin) + orange(row), margin + wmW));
575
+ writeln(boxRow('', 0));
576
+ const lead = margin + Math.floor((innerW - 2 * margin - vlen(CARD_TAGLINE)) / 2);
577
+ writeln(boxRow(rep(' ', lead) + dim(CARD_TAGLINE), lead + vlen(CARD_TAGLINE)));
578
+ writeln(boxRow('', 0));
579
+ writeDescRows(boxRow, orange, innerW - 2 * margin, margin);
580
+ writeln(boxRow('', 0));
581
+ writeln(frame('└' + rep('─', innerW) + '┘'));
535
582
  cardBelow({ orange, dim, green });
536
583
  }
537
584
 
538
585
  // cardBelow prints the description, sign-in line, cwd, and get-started hints.
539
586
  function cardBelow({ orange, dim, green }) {
540
- writeln();
541
- writeln('Swytchcode sits between your agent and any API — auth, retries, idempotency, and schema');
542
- writeln(`validation, handled. Run a command below, or ${orange('swytchcode --help')} for the full list.`);
543
587
  writeln();
544
588
  writeln(`${orange('●')} ${dim('not signed in — run ')}${orange('swytchcode login')}`);
545
589
  const cwd = process.cwd().replace(/\\/g, '/');
@@ -575,6 +619,12 @@ async function runOnboarding() {
575
619
 
576
620
  welcomeCard(cliVersion);
577
621
 
622
+ // Divider between the welcome card/info and the interactive walkthrough,
623
+ // matching the rules drawn between steps below.
624
+ writeln();
625
+ writeln(' ' + '─'.repeat(45));
626
+ writeln();
627
+
578
628
  // ── Step 1: Demo ──────────────────────────────────────────────────────────
579
629
  writeln(`${c.bold} Step 1 of 4 — See it in action${c.reset}`);
580
630
  writeln(`${c.dim} Run a live API call right now, no setup required.${c.reset}`);
@@ -591,12 +641,11 @@ async function runOnboardingSteps2to4() {
591
641
  writeln(`${c.bold} Step 2 of 4 — Install Swytchcode${c.reset}`);
592
642
  writeln(`${c.dim} Install once, use everywhere. Adds the ${c.reset}${c.cyan}swytchcode${c.reset}${c.dim} command to your terminal.${c.reset}`);
593
643
  writeln();
594
- writeln(` ${c.dim}Installing into: ${process.cwd()}${c.reset}`);
595
- writeln(` ${c.dim}$ npm install swytchcode${c.reset}`);
644
+ writeln(` ${c.dim}$ npm install -g swytchcode${c.reset}`);
596
645
  writeln();
597
646
 
598
- const installChoice = await menuArrow('Install here?', [
599
- { label: 'Yes, install here', value: 'yes' },
647
+ const installChoice = await menuArrow('Install globally?', [
648
+ { label: 'Yes, install globally', value: 'yes' },
600
649
  { label: 'Skip for now', value: 'no' },
601
650
  ]);
602
651
 
@@ -604,12 +653,11 @@ async function runOnboardingSteps2to4() {
604
653
 
605
654
  if (installChoice === 'no') {
606
655
  writeln(` ${c.dim}Skipped. Install manually with:${c.reset}`);
607
- writeln(` ${c.cyan}npm install swytchcode${c.reset}`);
608
- writeln(` ${c.dim}For global access:${c.reset} ${c.cyan}npm install -g swytchcode${c.reset}`);
656
+ writeln(` ${c.cyan}npm install -g swytchcode${c.reset}`);
609
657
  } else {
610
- const installSpin = spinner('Running npm install swytchcode ...');
658
+ const installSpin = spinner('Running npm install -g swytchcode ...');
611
659
  try {
612
- execSync('npm install swytchcode', { stdio: 'pipe' });
660
+ execSync('npm install -g swytchcode', { stdio: 'pipe' });
613
661
  installSpin.stop('installed');
614
662
  } catch (e) {
615
663
  if (e.signal === 'SIGINT') {
@@ -620,10 +668,6 @@ async function runOnboardingSteps2to4() {
620
668
  }
621
669
  installSpin.fail('Installation failed.');
622
670
  }
623
-
624
- writeln();
625
- writeln(` ${c.dim}To make it available globally, run:${c.reset}`);
626
- writeln(` ${c.bold}${c.green}npm install -g swytchcode${c.reset}`);
627
671
  }
628
672
 
629
673
  // ── Step 3: Account sync ──────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swytchcode",
3
- "version": "2.14.0-beta.1",
3
+ "version": "2.14.0-beta.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://gitlab.com/swytchcode/cli.git"
@@ -28,11 +28,11 @@
28
28
  "README.md"
29
29
  ],
30
30
  "optionalDependencies": {
31
- "swytchcode-cli-darwin-arm64": "2.14.0-beta.1",
32
- "swytchcode-cli-darwin-x64": "2.14.0-beta.1",
33
- "swytchcode-cli-linux-arm64": "2.14.0-beta.1",
34
- "swytchcode-cli-linux-x64": "2.14.0-beta.1",
35
- "swytchcode-cli-win32-arm64": "2.14.0-beta.1",
36
- "swytchcode-cli-win32-x64": "2.14.0-beta.1"
31
+ "swytchcode-cli-darwin-arm64": "2.14.0-beta.2",
32
+ "swytchcode-cli-darwin-x64": "2.14.0-beta.2",
33
+ "swytchcode-cli-linux-arm64": "2.14.0-beta.2",
34
+ "swytchcode-cli-linux-x64": "2.14.0-beta.2",
35
+ "swytchcode-cli-win32-arm64": "2.14.0-beta.2",
36
+ "swytchcode-cli-win32-x64": "2.14.0-beta.2"
37
37
  }
38
38
  }