@sideboard-ai/core 0.1.36 → 0.1.38

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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  cursorSdkMessageToEvents,
4
4
  formatUnknownDetail
5
- } from "../chunk-BSZX63TV.js";
5
+ } from "../chunk-PU27NUO4.js";
6
6
  import {
7
7
  appDataDir
8
8
  } from "../chunk-M37RITA6.js";
@@ -21,12 +21,12 @@ import {
21
21
  opencodeAdapter,
22
22
  permissionMode,
23
23
  resolveCursorModelId
24
- } from "./chunk-EHUIPKBW.js";
24
+ } from "./chunk-BEXVE7LX.js";
25
25
  import "./chunk-ILQK4P5R.js";
26
26
  import {
27
27
  cursorSdkMessageToEvents,
28
28
  parseCursorRunnerLine
29
- } from "./chunk-BSZX63TV.js";
29
+ } from "./chunk-PU27NUO4.js";
30
30
  import "./chunk-YZ23S32T.js";
31
31
  import "./chunk-M37RITA6.js";
32
32
  import {
@@ -10,7 +10,7 @@ import {
10
10
  formatUnknownDetail,
11
11
  looksLikeAgentFailureMessage,
12
12
  parseCursorRunnerLine
13
- } from "./chunk-BSZX63TV.js";
13
+ } from "./chunk-PU27NUO4.js";
14
14
  import {
15
15
  claudeChromeEnabled,
16
16
  loadAppSettings,
@@ -434,6 +434,30 @@ import { createRequire } from "module";
434
434
  import { tmpdir } from "os";
435
435
  import { dirname, join } from "path";
436
436
  import { fileURLToPath } from "url";
437
+
438
+ // src/agents/node-launch.ts
439
+ function isAsarPath(filePath) {
440
+ return /\.asar([/\\]|$)/.test(filePath);
441
+ }
442
+ async function resolveNodeLaunch(scriptPath) {
443
+ if (isAsarPath(scriptPath)) {
444
+ return {
445
+ file: process.execPath,
446
+ env: { ELECTRON_RUN_AS_NODE: "1" }
447
+ };
448
+ }
449
+ const whichNode = await run("which", ["node"], { reject: false });
450
+ const nodeBin = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : null;
451
+ if (nodeBin) {
452
+ return { file: nodeBin, env: {} };
453
+ }
454
+ return {
455
+ file: process.execPath,
456
+ env: { ELECTRON_RUN_AS_NODE: "1" }
457
+ };
458
+ }
459
+
460
+ // src/agents/injected-mcp.ts
437
461
  var SIDEBOARD_MCP_ALLOWED_TOOLS = [
438
462
  "mcp__sideboard",
439
463
  "mcp__sideboard__*"
@@ -536,11 +560,13 @@ async function resolveSideboardMcpServer() {
536
560
  const entry = findSideboardMcpJsEntry();
537
561
  if (entry) {
538
562
  const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
563
+ const scriptArgs = isCli ? [entry, "mcp"] : [entry];
564
+ const launch = await resolveNodeLaunch(entry);
539
565
  return {
540
566
  name: "sideboard",
541
- // Use `node` (not process.execPath) — under Electron execPath is Electron itself.
542
- command: "node",
543
- args: isCli ? [entry, "mcp"] : [entry]
567
+ command: launch.file,
568
+ args: scriptArgs,
569
+ ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
544
570
  };
545
571
  }
546
572
  const which = await run("which", ["sideboard"], { reject: false });
@@ -772,7 +798,7 @@ var claudeAdapter = {
772
798
  );
773
799
  }
774
800
  const mode = permissionMode(thread);
775
- const { isOrchestratorThread } = await import("./global-workspace-LM4AA4RO.js");
801
+ const { isOrchestratorThread } = await import("./global-workspace-QSRP25HQ.js");
776
802
  const isOrchestrator = isOrchestratorThread(thread);
777
803
  const injectedServers = await buildInjectedMcpServers({
778
804
  includeSideboard: true,
@@ -1324,16 +1350,14 @@ var cursorAdapter = {
1324
1350
  };
1325
1351
  const runner = cursorRunnerPath();
1326
1352
  const isTs = runner.endsWith(".ts");
1327
- const whichNode = await run("which", ["node"], { reject: false });
1328
- const nodeBin = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : null;
1329
- const file = nodeBin || process.execPath;
1353
+ const launch = await resolveNodeLaunch(runner);
1330
1354
  return {
1331
- file,
1355
+ file: launch.file,
1332
1356
  args: isTs ? ["--import", "tsx", runner] : [runner],
1333
1357
  cwd: thread.worktreePath,
1334
1358
  stdin: JSON.stringify(req),
1335
1359
  env: {
1336
- ...nodeBin ? {} : { ELECTRON_RUN_AS_NODE: "1" },
1360
+ ...launch.env,
1337
1361
  ...apiKey ? { CURSOR_API_KEY: apiKey } : {}
1338
1362
  }
1339
1363
  };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  isGlobalRepoPath
3
- } from "./chunk-Y2EWQ4TL.js";
3
+ } from "./chunk-IS3AGU33.js";
4
4
  import {
5
5
  ensureGhPreferOrigin,
6
6
  resolveRepoRoot
7
- } from "./chunk-UFWEANLU.js";
7
+ } from "./chunk-XBEQI5H4.js";
8
8
  import {
9
9
  appDataDir
10
10
  } from "./chunk-M37RITA6.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ensureGlobalCoordinatorCwd
3
- } from "./chunk-BMB7WCGF.js";
3
+ } from "./chunk-PTASB7SJ.js";
4
4
  import {
5
5
  allocateTeamName,
6
6
  takenSlugsFromThread,
7
7
  teamSlugFromName
8
- } from "./chunk-UFWEANLU.js";
8
+ } from "./chunk-XBEQI5H4.js";
9
9
  import {
10
10
  createEmptyThread,
11
11
  listThreads,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveGithubRepoSlug
3
- } from "./chunk-UFWEANLU.js";
3
+ } from "./chunk-XBEQI5H4.js";
4
4
  import {
5
5
  globalAgentCwd,
6
6
  sideboardReposDir
@@ -52,6 +52,10 @@ function pushTurnStderr(tail, line, maxLines = 12) {
52
52
  }
53
53
  function summarizeTurnStderr(tail, maxChars = 500) {
54
54
  if (tail.length === 0) return "";
55
+ const moduleMissing = [...tail].reverse().find((line) => /cannot find module/i.test(line));
56
+ if (moduleMissing) {
57
+ return moduleMissing.length <= maxChars ? moduleMissing : moduleMissing.slice(0, maxChars);
58
+ }
55
59
  const joined = tail.slice(-6).join("\n").trim();
56
60
  if (joined.length <= maxChars) return joined;
57
61
  return joined.slice(joined.length - maxChars);
@@ -3,7 +3,7 @@ import {
3
3
  ensureWorkspace,
4
4
  removeWorkspace,
5
5
  syncWorkspacesFromThreads
6
- } from "./chunk-PER4N6LS.js";
6
+ } from "./chunk-FVGRUZHI.js";
7
7
  import {
8
8
  GLOBAL_WORKSPACE_ID,
9
9
  createGlobalChat,
@@ -13,26 +13,26 @@ import {
13
13
  isGlobalThread,
14
14
  isOrchestratorThread,
15
15
  orchestratorSessionPoisonedByBuiltins
16
- } from "./chunk-Y2EWQ4TL.js";
16
+ } from "./chunk-IS3AGU33.js";
17
17
  import {
18
18
  coordinatorSystemPrompt,
19
19
  coordinatorTurnReminder,
20
20
  enrichWorkspacesWithGithub,
21
21
  ensureGlobalCoordinatorCwd
22
- } from "./chunk-BMB7WCGF.js";
22
+ } from "./chunk-PTASB7SJ.js";
23
23
  import {
24
24
  PLAN_MODE_INSTRUCTION,
25
25
  allAdapters,
26
26
  getAdapter,
27
27
  parseBrightsyCliLine
28
- } from "./chunk-EHUIPKBW.js";
28
+ } from "./chunk-BEXVE7LX.js";
29
29
  import {
30
30
  fallbackTurnFailDetail,
31
31
  formatTurnExitError,
32
32
  looksLikeAgentFailureMessage,
33
33
  pushTurnStderr,
34
34
  summarizeTurnStderr
35
- } from "./chunk-BSZX63TV.js";
35
+ } from "./chunk-PU27NUO4.js";
36
36
  import {
37
37
  childEnvWithAppSettings,
38
38
  getLinearApiKey,
@@ -71,7 +71,7 @@ import {
71
71
  takenSlugsFromThread,
72
72
  threadDisplayLabel,
73
73
  worktreeNameFromPath
74
- } from "./chunk-UFWEANLU.js";
74
+ } from "./chunk-XBEQI5H4.js";
75
75
  import {
76
76
  appendMessage,
77
77
  createEmptyThread,
@@ -451,9 +451,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
451
451
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
452
452
  );
453
453
  }
454
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-LM4AA4RO.js");
454
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-QSRP25HQ.js");
455
455
  if (isGlobalThread2(thread)) {
456
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-QH35ES7Y.js");
456
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-WIYQVMOG.js");
457
457
  ensureGlobalCoordinatorCwd2();
458
458
  }
459
459
  const adapter = getAdapter(thread.agent);
@@ -2606,7 +2606,7 @@ async function createThread(input, onSetupLine) {
2606
2606
  return readThread(thread.id) ?? thread;
2607
2607
  }
2608
2608
  async function listLinearIssues(agent, repoPath) {
2609
- const { getAdapter: getAdapter2 } = await import("./agents-JZK6TQG7.js");
2609
+ const { getAdapter: getAdapter2 } = await import("./agents-DDW4NBBW.js");
2610
2610
  await requireAgent(agent, { requireLinear: true });
2611
2611
  const adapter = getAdapter2(agent);
2612
2612
  if (!adapter.listLinearIssues) {
@@ -2826,7 +2826,7 @@ async function adoptThread(input) {
2826
2826
  messages: input.messages ?? []
2827
2827
  });
2828
2828
  writeThread(thread);
2829
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-23GHLR7I.js");
2829
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-DMYWHVJC.js");
2830
2830
  await ensureWorkspace2(repoPath);
2831
2831
  return thread;
2832
2832
  }
@@ -4331,7 +4331,7 @@ var Orchestrator = class {
4331
4331
  return setStatus(thread.id, "idle");
4332
4332
  }
4333
4333
  if (!existsSync11(thread.worktreePath)) {
4334
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-RYTBDHHP.js");
4334
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-37FBII5A.js");
4335
4335
  const { execa: execa6 } = await import("execa");
4336
4336
  const slug = thread.worktreePath.split("/").pop();
4337
4337
  const dest = thread.worktreePath;
@@ -4414,7 +4414,7 @@ async function startOrchestration(opts) {
4414
4414
  sourceRef: "default",
4415
4415
  ...createOpts
4416
4416
  }).catch(async () => {
4417
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-RYTBDHHP.js");
4417
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-37FBII5A.js");
4418
4418
  const repo = await resolveRepoRoot2(repoPath);
4419
4419
  const def = await resolveDefaultBranch2(repo);
4420
4420
  return createThread({
@@ -1473,11 +1473,83 @@ async function getPrDetails(cwd, selector) {
1473
1473
  };
1474
1474
  }
1475
1475
  async function fetchPrHead(repoPath, number, localBranch) {
1476
- await git(
1477
- ["fetch", "origin", `pull/${number}/head:${localBranch}`],
1478
- repoPath,
1479
- { reject: false }
1480
- );
1476
+ const slug = await resolveGithubRepoSlug(repoPath);
1477
+ const refspec = `+pull/${number}/head:${localBranch}`;
1478
+ const errors = [];
1479
+ const tryFetch = async (remote) => {
1480
+ const result = await git(["fetch", remote, refspec], repoPath, {
1481
+ reject: false
1482
+ });
1483
+ if (result.exitCode === 0) return true;
1484
+ const detail = (result.stderr || result.stdout).trim();
1485
+ if (detail) errors.push(`${remote}: ${detail}`);
1486
+ return false;
1487
+ };
1488
+ let fetched = await tryFetch("origin");
1489
+ if (!fetched && slug) {
1490
+ fetched = await tryFetch(`https://github.com/${slug}.git`);
1491
+ }
1492
+ const localOk = async () => {
1493
+ const verify = await git(["rev-parse", "--verify", localBranch], repoPath, {
1494
+ reject: false
1495
+ });
1496
+ return verify.exitCode === 0;
1497
+ };
1498
+ if (!await localOk()) {
1499
+ const viewArgs = [
1500
+ "pr",
1501
+ "view",
1502
+ String(number),
1503
+ "--json",
1504
+ "headRefOid"
1505
+ ];
1506
+ if (slug) viewArgs.push("--repo", slug);
1507
+ const view = await gh(viewArgs, repoPath, { reject: false });
1508
+ let oid = "";
1509
+ if (view.exitCode === 0 && view.stdout.trim()) {
1510
+ try {
1511
+ oid = String(
1512
+ JSON.parse(view.stdout).headRefOid ?? ""
1513
+ ).trim();
1514
+ } catch {
1515
+ oid = "";
1516
+ }
1517
+ }
1518
+ if (oid) {
1519
+ const ensureOid = async (remote) => {
1520
+ const got = await git(["fetch", remote, oid], repoPath, {
1521
+ reject: false
1522
+ });
1523
+ return got.exitCode === 0;
1524
+ };
1525
+ let haveObject = (await git(["cat-file", "-e", `${oid}^{commit}`], repoPath, {
1526
+ reject: false
1527
+ })).exitCode === 0;
1528
+ if (!haveObject) haveObject = await ensureOid("origin");
1529
+ if (!haveObject && slug) {
1530
+ haveObject = await ensureOid(`https://github.com/${slug}.git`);
1531
+ }
1532
+ if (haveObject) {
1533
+ const branched = await git(["branch", "-f", localBranch, oid], repoPath, {
1534
+ reject: false
1535
+ });
1536
+ if (branched.exitCode !== 0) {
1537
+ const detail = (branched.stderr || branched.stdout).trim();
1538
+ if (detail) errors.push(`branch -f: ${detail}`);
1539
+ }
1540
+ } else {
1541
+ errors.push(`could not fetch commit ${oid.slice(0, 12)}`);
1542
+ }
1543
+ } else if (view.stderr.trim()) {
1544
+ errors.push(view.stderr.trim());
1545
+ }
1546
+ }
1547
+ if (!await localOk()) {
1548
+ const hint = errors.length ? ` (${errors.join(" | ")})` : "";
1549
+ throw new Error(
1550
+ `Failed to fetch PR #${number} head into ${localBranch}${hint}`
1551
+ );
1552
+ }
1481
1553
  }
1482
1554
  async function resolveWorktreeStartPoint(repoPath, sourceRef) {
1483
1555
  const ref = sourceRef.trim();
@@ -1487,6 +1559,7 @@ async function resolveWorktreeStartPoint(repoPath, sourceRef) {
1487
1559
  reject: false
1488
1560
  });
1489
1561
  if (ok.exitCode === 0) return ref;
1562
+ throw new Error(`Invalid git reference: ${ref}`);
1490
1563
  }
1491
1564
  const remote = `origin/${ref}`;
1492
1565
  const remoteOk = await git(["rev-parse", "--verify", remote], repoPath, {
@@ -1497,7 +1570,10 @@ async function resolveWorktreeStartPoint(repoPath, sourceRef) {
1497
1570
  reject: false
1498
1571
  });
1499
1572
  if (localOk.exitCode === 0) return ref;
1500
- return ref;
1573
+ throw new Error(`Invalid git reference: ${ref}`);
1574
+ }
1575
+ function isLocalPrFetchBranch(ref) {
1576
+ return /^sideboard-pr-\d+$/.test(ref.trim());
1501
1577
  }
1502
1578
  async function createThreadWorktree(opts) {
1503
1579
  let branchName = `thread/${opts.slug}`;
@@ -1507,7 +1583,7 @@ async function createThreadWorktree(opts) {
1507
1583
  }
1508
1584
  await ensureGhPreferOrigin(opts.repoPath);
1509
1585
  await git(["fetch", "origin", "--prune"], opts.repoPath, { reject: false });
1510
- if (!opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
1586
+ if (!isLocalPrFetchBranch(opts.sourceRef) && !opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
1511
1587
  await git(["fetch", "origin", opts.sourceRef], opts.repoPath, {
1512
1588
  reject: false
1513
1589
  });
@@ -6,8 +6,8 @@ import {
6
6
  enrichWorkspacesWithGithub,
7
7
  ensureGlobalCoordinatorCwd,
8
8
  formatWorkspaceInventory
9
- } from "./chunk-BMB7WCGF.js";
10
- import "./chunk-UFWEANLU.js";
9
+ } from "./chunk-PTASB7SJ.js";
10
+ import "./chunk-XBEQI5H4.js";
11
11
  import "./chunk-HYRHI3QU.js";
12
12
  import "./chunk-M37RITA6.js";
13
13
  import "./chunk-AJ6ROGD7.js";
@@ -11,9 +11,9 @@ import {
11
11
  orchestrationTitleNeedsSoccerNickname,
12
12
  orchestratorSessionPoisonedByBuiltins,
13
13
  takenTeamSlugsForOrchestration
14
- } from "./chunk-Y2EWQ4TL.js";
15
- import "./chunk-BMB7WCGF.js";
16
- import "./chunk-UFWEANLU.js";
14
+ } from "./chunk-IS3AGU33.js";
15
+ import "./chunk-PTASB7SJ.js";
16
+ import "./chunk-XBEQI5H4.js";
17
17
  import "./chunk-HYRHI3QU.js";
18
18
  import {
19
19
  globalAgentCwd
package/dist/index.cjs CHANGED
@@ -2579,11 +2579,83 @@ async function getPrDetails(cwd, selector) {
2579
2579
  };
2580
2580
  }
2581
2581
  async function fetchPrHead(repoPath, number, localBranch) {
2582
- await git(
2583
- ["fetch", "origin", `pull/${number}/head:${localBranch}`],
2584
- repoPath,
2585
- { reject: false }
2586
- );
2582
+ const slug = await resolveGithubRepoSlug(repoPath);
2583
+ const refspec = `+pull/${number}/head:${localBranch}`;
2584
+ const errors = [];
2585
+ const tryFetch = async (remote) => {
2586
+ const result = await git(["fetch", remote, refspec], repoPath, {
2587
+ reject: false
2588
+ });
2589
+ if (result.exitCode === 0) return true;
2590
+ const detail = (result.stderr || result.stdout).trim();
2591
+ if (detail) errors.push(`${remote}: ${detail}`);
2592
+ return false;
2593
+ };
2594
+ let fetched = await tryFetch("origin");
2595
+ if (!fetched && slug) {
2596
+ fetched = await tryFetch(`https://github.com/${slug}.git`);
2597
+ }
2598
+ const localOk = async () => {
2599
+ const verify = await git(["rev-parse", "--verify", localBranch], repoPath, {
2600
+ reject: false
2601
+ });
2602
+ return verify.exitCode === 0;
2603
+ };
2604
+ if (!await localOk()) {
2605
+ const viewArgs = [
2606
+ "pr",
2607
+ "view",
2608
+ String(number),
2609
+ "--json",
2610
+ "headRefOid"
2611
+ ];
2612
+ if (slug) viewArgs.push("--repo", slug);
2613
+ const view = await gh(viewArgs, repoPath, { reject: false });
2614
+ let oid = "";
2615
+ if (view.exitCode === 0 && view.stdout.trim()) {
2616
+ try {
2617
+ oid = String(
2618
+ JSON.parse(view.stdout).headRefOid ?? ""
2619
+ ).trim();
2620
+ } catch {
2621
+ oid = "";
2622
+ }
2623
+ }
2624
+ if (oid) {
2625
+ const ensureOid = async (remote) => {
2626
+ const got = await git(["fetch", remote, oid], repoPath, {
2627
+ reject: false
2628
+ });
2629
+ return got.exitCode === 0;
2630
+ };
2631
+ let haveObject = (await git(["cat-file", "-e", `${oid}^{commit}`], repoPath, {
2632
+ reject: false
2633
+ })).exitCode === 0;
2634
+ if (!haveObject) haveObject = await ensureOid("origin");
2635
+ if (!haveObject && slug) {
2636
+ haveObject = await ensureOid(`https://github.com/${slug}.git`);
2637
+ }
2638
+ if (haveObject) {
2639
+ const branched = await git(["branch", "-f", localBranch, oid], repoPath, {
2640
+ reject: false
2641
+ });
2642
+ if (branched.exitCode !== 0) {
2643
+ const detail = (branched.stderr || branched.stdout).trim();
2644
+ if (detail) errors.push(`branch -f: ${detail}`);
2645
+ }
2646
+ } else {
2647
+ errors.push(`could not fetch commit ${oid.slice(0, 12)}`);
2648
+ }
2649
+ } else if (view.stderr.trim()) {
2650
+ errors.push(view.stderr.trim());
2651
+ }
2652
+ }
2653
+ if (!await localOk()) {
2654
+ const hint = errors.length ? ` (${errors.join(" | ")})` : "";
2655
+ throw new Error(
2656
+ `Failed to fetch PR #${number} head into ${localBranch}${hint}`
2657
+ );
2658
+ }
2587
2659
  }
2588
2660
  async function resolveWorktreeStartPoint(repoPath, sourceRef) {
2589
2661
  const ref = sourceRef.trim();
@@ -2593,6 +2665,7 @@ async function resolveWorktreeStartPoint(repoPath, sourceRef) {
2593
2665
  reject: false
2594
2666
  });
2595
2667
  if (ok.exitCode === 0) return ref;
2668
+ throw new Error(`Invalid git reference: ${ref}`);
2596
2669
  }
2597
2670
  const remote = `origin/${ref}`;
2598
2671
  const remoteOk = await git(["rev-parse", "--verify", remote], repoPath, {
@@ -2603,7 +2676,10 @@ async function resolveWorktreeStartPoint(repoPath, sourceRef) {
2603
2676
  reject: false
2604
2677
  });
2605
2678
  if (localOk.exitCode === 0) return ref;
2606
- return ref;
2679
+ throw new Error(`Invalid git reference: ${ref}`);
2680
+ }
2681
+ function isLocalPrFetchBranch(ref) {
2682
+ return /^sideboard-pr-\d+$/.test(ref.trim());
2607
2683
  }
2608
2684
  async function createThreadWorktree(opts) {
2609
2685
  let branchName = `thread/${opts.slug}`;
@@ -2613,7 +2689,7 @@ async function createThreadWorktree(opts) {
2613
2689
  }
2614
2690
  await ensureGhPreferOrigin(opts.repoPath);
2615
2691
  await git(["fetch", "origin", "--prune"], opts.repoPath, { reject: false });
2616
- if (!opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
2692
+ if (!isLocalPrFetchBranch(opts.sourceRef) && !opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
2617
2693
  await git(["fetch", "origin", opts.sourceRef], opts.repoPath, {
2618
2694
  reject: false
2619
2695
  });
@@ -3721,6 +3797,10 @@ function pushTurnStderr(tail, line, maxLines = 12) {
3721
3797
  }
3722
3798
  function summarizeTurnStderr(tail, maxChars = 500) {
3723
3799
  if (tail.length === 0) return "";
3800
+ const moduleMissing = [...tail].reverse().find((line) => /cannot find module/i.test(line));
3801
+ if (moduleMissing) {
3802
+ return moduleMissing.length <= maxChars ? moduleMissing : moduleMissing.slice(0, maxChars);
3803
+ }
3724
3804
  const joined = tail.slice(-6).join("\n").trim();
3725
3805
  if (joined.length <= maxChars) return joined;
3726
3806
  return joined.slice(joined.length - maxChars);
@@ -4180,6 +4260,34 @@ var init_claude_mcp = __esm({
4180
4260
  }
4181
4261
  });
4182
4262
 
4263
+ // src/agents/node-launch.ts
4264
+ function isAsarPath(filePath) {
4265
+ return /\.asar([/\\]|$)/.test(filePath);
4266
+ }
4267
+ async function resolveNodeLaunch(scriptPath) {
4268
+ if (isAsarPath(scriptPath)) {
4269
+ return {
4270
+ file: process.execPath,
4271
+ env: { ELECTRON_RUN_AS_NODE: "1" }
4272
+ };
4273
+ }
4274
+ const whichNode = await run("which", ["node"], { reject: false });
4275
+ const nodeBin = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : null;
4276
+ if (nodeBin) {
4277
+ return { file: nodeBin, env: {} };
4278
+ }
4279
+ return {
4280
+ file: process.execPath,
4281
+ env: { ELECTRON_RUN_AS_NODE: "1" }
4282
+ };
4283
+ }
4284
+ var init_node_launch = __esm({
4285
+ "src/agents/node-launch.ts"() {
4286
+ "use strict";
4287
+ init_run();
4288
+ }
4289
+ });
4290
+
4183
4291
  // src/agents/injected-mcp.ts
4184
4292
  async function resolveBrightsyMcpCommand() {
4185
4293
  const now = Date.now();
@@ -4269,11 +4377,13 @@ async function resolveSideboardMcpServer() {
4269
4377
  const entry = findSideboardMcpJsEntry();
4270
4378
  if (entry) {
4271
4379
  const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
4380
+ const scriptArgs = isCli ? [entry, "mcp"] : [entry];
4381
+ const launch = await resolveNodeLaunch(entry);
4272
4382
  return {
4273
4383
  name: "sideboard",
4274
- // Use `node` (not process.execPath) — under Electron execPath is Electron itself.
4275
- command: "node",
4276
- args: isCli ? [entry, "mcp"] : [entry]
4384
+ command: launch.file,
4385
+ args: scriptArgs,
4386
+ ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
4277
4387
  };
4278
4388
  }
4279
4389
  const which = await run("which", ["sideboard"], { reject: false });
@@ -4334,6 +4444,7 @@ var init_injected_mcp = __esm({
4334
4444
  init_run();
4335
4445
  init_config();
4336
4446
  init_connected_teams();
4447
+ init_node_launch();
4337
4448
  import_meta = {};
4338
4449
  SIDEBOARD_MCP_ALLOWED_TOOLS = [
4339
4450
  "mcp__sideboard",
@@ -5165,6 +5276,7 @@ var init_cursor = __esm({
5165
5276
  init_run();
5166
5277
  init_app_settings();
5167
5278
  init_cursor_events();
5279
+ init_node_launch();
5168
5280
  init_turn_input();
5169
5281
  init_cursor_events();
5170
5282
  import_meta2 = {};
@@ -5225,16 +5337,14 @@ var init_cursor = __esm({
5225
5337
  };
5226
5338
  const runner = cursorRunnerPath();
5227
5339
  const isTs = runner.endsWith(".ts");
5228
- const whichNode = await run("which", ["node"], { reject: false });
5229
- const nodeBin = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : null;
5230
- const file = nodeBin || process.execPath;
5340
+ const launch = await resolveNodeLaunch(runner);
5231
5341
  return {
5232
- file,
5342
+ file: launch.file,
5233
5343
  args: isTs ? ["--import", "tsx", runner] : [runner],
5234
5344
  cwd: thread.worktreePath,
5235
5345
  stdin: JSON.stringify(req),
5236
5346
  env: {
5237
- ...nodeBin ? {} : { ELECTRON_RUN_AS_NODE: "1" },
5347
+ ...launch.env,
5238
5348
  ...apiKey ? { CURSOR_API_KEY: apiKey } : {}
5239
5349
  }
5240
5350
  };
package/dist/index.js CHANGED
@@ -101,14 +101,14 @@ import {
101
101
  withAgentInstructions,
102
102
  worktreeCleanupSettings,
103
103
  writeWorktreeFile
104
- } from "./chunk-KSVPOIRV.js";
104
+ } from "./chunk-UKDAGGTU.js";
105
105
  import {
106
106
  addWorkspace,
107
107
  ensureWorkspace,
108
108
  listWorkspaces,
109
109
  removeWorkspace,
110
110
  syncWorkspacesFromThreads
111
- } from "./chunk-PER4N6LS.js";
111
+ } from "./chunk-FVGRUZHI.js";
112
112
  import {
113
113
  CLOUD_COORDINATOR_BUSY_REPLY,
114
114
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -128,7 +128,7 @@ import {
128
128
  orchestratorSessionPoisonedByBuiltins,
129
129
  parseForceStopMessage,
130
130
  takenTeamSlugsForOrchestration
131
- } from "./chunk-Y2EWQ4TL.js";
131
+ } from "./chunk-IS3AGU33.js";
132
132
  import {
133
133
  COORDINATOR_TOOL_PLAYBOOK,
134
134
  coordinatorSystemPrompt,
@@ -136,7 +136,7 @@ import {
136
136
  enrichWorkspacesWithGithub,
137
137
  ensureGlobalCoordinatorCwd,
138
138
  formatWorkspaceInventory
139
- } from "./chunk-BMB7WCGF.js";
139
+ } from "./chunk-PTASB7SJ.js";
140
140
  import {
141
141
  BRIGHTSY_MCP_ALLOWED_TOOLS,
142
142
  MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
@@ -176,7 +176,7 @@ import {
176
176
  resolveCursorModelId,
177
177
  sanitizeMcpServerName,
178
178
  writeInjectedMcpConfig
179
- } from "./chunk-EHUIPKBW.js";
179
+ } from "./chunk-BEXVE7LX.js";
180
180
  import {
181
181
  brightsyConfigPath,
182
182
  brightsyMcpServerName,
@@ -192,7 +192,7 @@ import {
192
192
  import {
193
193
  cursorSdkMessageToEvents,
194
194
  parseCursorRunnerLine
195
- } from "./chunk-BSZX63TV.js";
195
+ } from "./chunk-PU27NUO4.js";
196
196
  import {
197
197
  HARNESS_ENV_KEYS,
198
198
  appSettingsPath,
@@ -275,7 +275,7 @@ import {
275
275
  worktreeDisplayLabel,
276
276
  worktreeDisplayLabelForGroup,
277
277
  worktreeNameFromPath
278
- } from "./chunk-UFWEANLU.js";
278
+ } from "./chunk-XBEQI5H4.js";
279
279
  import {
280
280
  appendMessage,
281
281
  createEmptyThread,
@@ -83,6 +83,10 @@ function pushTurnStderr(tail, line, maxLines = 12) {
83
83
  }
84
84
  function summarizeTurnStderr(tail, maxChars = 500) {
85
85
  if (tail.length === 0) return "";
86
+ const moduleMissing = [...tail].reverse().find((line) => /cannot find module/i.test(line));
87
+ if (moduleMissing) {
88
+ return moduleMissing.length <= maxChars ? moduleMissing : moduleMissing.slice(0, maxChars);
89
+ }
86
90
  const joined = tail.slice(-6).join("\n").trim();
87
91
  if (joined.length <= maxChars) return joined;
88
92
  return joined.slice(joined.length - maxChars);
@@ -2174,11 +2178,83 @@ async function getPrDetails(cwd, selector) {
2174
2178
  };
2175
2179
  }
2176
2180
  async function fetchPrHead(repoPath, number, localBranch) {
2177
- await git(
2178
- ["fetch", "origin", `pull/${number}/head:${localBranch}`],
2179
- repoPath,
2180
- { reject: false }
2181
- );
2181
+ const slug = await resolveGithubRepoSlug(repoPath);
2182
+ const refspec = `+pull/${number}/head:${localBranch}`;
2183
+ const errors = [];
2184
+ const tryFetch = async (remote) => {
2185
+ const result = await git(["fetch", remote, refspec], repoPath, {
2186
+ reject: false
2187
+ });
2188
+ if (result.exitCode === 0) return true;
2189
+ const detail = (result.stderr || result.stdout).trim();
2190
+ if (detail) errors.push(`${remote}: ${detail}`);
2191
+ return false;
2192
+ };
2193
+ let fetched = await tryFetch("origin");
2194
+ if (!fetched && slug) {
2195
+ fetched = await tryFetch(`https://github.com/${slug}.git`);
2196
+ }
2197
+ const localOk = async () => {
2198
+ const verify = await git(["rev-parse", "--verify", localBranch], repoPath, {
2199
+ reject: false
2200
+ });
2201
+ return verify.exitCode === 0;
2202
+ };
2203
+ if (!await localOk()) {
2204
+ const viewArgs = [
2205
+ "pr",
2206
+ "view",
2207
+ String(number),
2208
+ "--json",
2209
+ "headRefOid"
2210
+ ];
2211
+ if (slug) viewArgs.push("--repo", slug);
2212
+ const view = await gh(viewArgs, repoPath, { reject: false });
2213
+ let oid = "";
2214
+ if (view.exitCode === 0 && view.stdout.trim()) {
2215
+ try {
2216
+ oid = String(
2217
+ JSON.parse(view.stdout).headRefOid ?? ""
2218
+ ).trim();
2219
+ } catch {
2220
+ oid = "";
2221
+ }
2222
+ }
2223
+ if (oid) {
2224
+ const ensureOid = async (remote) => {
2225
+ const got = await git(["fetch", remote, oid], repoPath, {
2226
+ reject: false
2227
+ });
2228
+ return got.exitCode === 0;
2229
+ };
2230
+ let haveObject = (await git(["cat-file", "-e", `${oid}^{commit}`], repoPath, {
2231
+ reject: false
2232
+ })).exitCode === 0;
2233
+ if (!haveObject) haveObject = await ensureOid("origin");
2234
+ if (!haveObject && slug) {
2235
+ haveObject = await ensureOid(`https://github.com/${slug}.git`);
2236
+ }
2237
+ if (haveObject) {
2238
+ const branched = await git(["branch", "-f", localBranch, oid], repoPath, {
2239
+ reject: false
2240
+ });
2241
+ if (branched.exitCode !== 0) {
2242
+ const detail = (branched.stderr || branched.stdout).trim();
2243
+ if (detail) errors.push(`branch -f: ${detail}`);
2244
+ }
2245
+ } else {
2246
+ errors.push(`could not fetch commit ${oid.slice(0, 12)}`);
2247
+ }
2248
+ } else if (view.stderr.trim()) {
2249
+ errors.push(view.stderr.trim());
2250
+ }
2251
+ }
2252
+ if (!await localOk()) {
2253
+ const hint = errors.length ? ` (${errors.join(" | ")})` : "";
2254
+ throw new Error(
2255
+ `Failed to fetch PR #${number} head into ${localBranch}${hint}`
2256
+ );
2257
+ }
2182
2258
  }
2183
2259
  async function resolveWorktreeStartPoint(repoPath, sourceRef) {
2184
2260
  const ref = sourceRef.trim();
@@ -2188,6 +2264,7 @@ async function resolveWorktreeStartPoint(repoPath, sourceRef) {
2188
2264
  reject: false
2189
2265
  });
2190
2266
  if (ok.exitCode === 0) return ref;
2267
+ throw new Error(`Invalid git reference: ${ref}`);
2191
2268
  }
2192
2269
  const remote = `origin/${ref}`;
2193
2270
  const remoteOk = await git(["rev-parse", "--verify", remote], repoPath, {
@@ -2198,7 +2275,10 @@ async function resolveWorktreeStartPoint(repoPath, sourceRef) {
2198
2275
  reject: false
2199
2276
  });
2200
2277
  if (localOk.exitCode === 0) return ref;
2201
- return ref;
2278
+ throw new Error(`Invalid git reference: ${ref}`);
2279
+ }
2280
+ function isLocalPrFetchBranch(ref) {
2281
+ return /^sideboard-pr-\d+$/.test(ref.trim());
2202
2282
  }
2203
2283
  async function createThreadWorktree(opts) {
2204
2284
  let branchName = `thread/${opts.slug}`;
@@ -2208,7 +2288,7 @@ async function createThreadWorktree(opts) {
2208
2288
  }
2209
2289
  await ensureGhPreferOrigin(opts.repoPath);
2210
2290
  await git(["fetch", "origin", "--prune"], opts.repoPath, { reject: false });
2211
- if (!opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
2291
+ if (!isLocalPrFetchBranch(opts.sourceRef) && !opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
2212
2292
  await git(["fetch", "origin", opts.sourceRef], opts.repoPath, {
2213
2293
  reject: false
2214
2294
  });
@@ -3826,6 +3906,34 @@ var init_claude_mcp = __esm({
3826
3906
  }
3827
3907
  });
3828
3908
 
3909
+ // src/agents/node-launch.ts
3910
+ function isAsarPath(filePath) {
3911
+ return /\.asar([/\\]|$)/.test(filePath);
3912
+ }
3913
+ async function resolveNodeLaunch(scriptPath) {
3914
+ if (isAsarPath(scriptPath)) {
3915
+ return {
3916
+ file: process.execPath,
3917
+ env: { ELECTRON_RUN_AS_NODE: "1" }
3918
+ };
3919
+ }
3920
+ const whichNode = await run("which", ["node"], { reject: false });
3921
+ const nodeBin = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : null;
3922
+ if (nodeBin) {
3923
+ return { file: nodeBin, env: {} };
3924
+ }
3925
+ return {
3926
+ file: process.execPath,
3927
+ env: { ELECTRON_RUN_AS_NODE: "1" }
3928
+ };
3929
+ }
3930
+ var init_node_launch = __esm({
3931
+ "src/agents/node-launch.ts"() {
3932
+ "use strict";
3933
+ init_run();
3934
+ }
3935
+ });
3936
+
3829
3937
  // src/agents/injected-mcp.ts
3830
3938
  async function resolveBrightsyMcpCommand() {
3831
3939
  const now = Date.now();
@@ -3915,11 +4023,13 @@ async function resolveSideboardMcpServer() {
3915
4023
  const entry = findSideboardMcpJsEntry();
3916
4024
  if (entry) {
3917
4025
  const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
4026
+ const scriptArgs = isCli ? [entry, "mcp"] : [entry];
4027
+ const launch = await resolveNodeLaunch(entry);
3918
4028
  return {
3919
4029
  name: "sideboard",
3920
- // Use `node` (not process.execPath) — under Electron execPath is Electron itself.
3921
- command: "node",
3922
- args: isCli ? [entry, "mcp"] : [entry]
4030
+ command: launch.file,
4031
+ args: scriptArgs,
4032
+ ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
3923
4033
  };
3924
4034
  }
3925
4035
  const which = await run("which", ["sideboard"], { reject: false });
@@ -3977,6 +4087,7 @@ var init_injected_mcp = __esm({
3977
4087
  init_run();
3978
4088
  init_config();
3979
4089
  init_connected_teams();
4090
+ init_node_launch();
3980
4091
  import_meta = {};
3981
4092
  SIDEBOARD_MCP_ALLOWED_TOOLS = [
3982
4093
  "mcp__sideboard",
@@ -4804,6 +4915,7 @@ var init_cursor = __esm({
4804
4915
  init_run();
4805
4916
  init_app_settings();
4806
4917
  init_cursor_events();
4918
+ init_node_launch();
4807
4919
  init_turn_input();
4808
4920
  init_cursor_events();
4809
4921
  import_meta2 = {};
@@ -4864,16 +4976,14 @@ var init_cursor = __esm({
4864
4976
  };
4865
4977
  const runner = cursorRunnerPath();
4866
4978
  const isTs = runner.endsWith(".ts");
4867
- const whichNode = await run("which", ["node"], { reject: false });
4868
- const nodeBin = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : null;
4869
- const file = nodeBin || process.execPath;
4979
+ const launch = await resolveNodeLaunch(runner);
4870
4980
  return {
4871
- file,
4981
+ file: launch.file,
4872
4982
  args: isTs ? ["--import", "tsx", runner] : [runner],
4873
4983
  cwd: thread.worktreePath,
4874
4984
  stdin: JSON.stringify(req),
4875
4985
  env: {
4876
- ...nodeBin ? {} : { ELECTRON_RUN_AS_NODE: "1" },
4986
+ ...launch.env,
4877
4987
  ...apiKey ? { CURSOR_API_KEY: apiKey } : {}
4878
4988
  }
4879
4989
  };
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startMcpServer
4
- } from "../chunk-KSVPOIRV.js";
5
- import "../chunk-PER4N6LS.js";
6
- import "../chunk-Y2EWQ4TL.js";
7
- import "../chunk-BMB7WCGF.js";
8
- import "../chunk-EHUIPKBW.js";
4
+ } from "../chunk-UKDAGGTU.js";
5
+ import "../chunk-FVGRUZHI.js";
6
+ import "../chunk-IS3AGU33.js";
7
+ import "../chunk-PTASB7SJ.js";
8
+ import "../chunk-BEXVE7LX.js";
9
9
  import "../chunk-ILQK4P5R.js";
10
- import "../chunk-BSZX63TV.js";
10
+ import "../chunk-PU27NUO4.js";
11
11
  import "../chunk-YZ23S32T.js";
12
- import "../chunk-UFWEANLU.js";
12
+ import "../chunk-XBEQI5H4.js";
13
13
  import "../chunk-HYRHI3QU.js";
14
14
  import "../chunk-M37RITA6.js";
15
15
  import "../chunk-AJ6ROGD7.js";
@@ -4,10 +4,10 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-PER4N6LS.js";
8
- import "./chunk-Y2EWQ4TL.js";
9
- import "./chunk-BMB7WCGF.js";
10
- import "./chunk-UFWEANLU.js";
7
+ } from "./chunk-FVGRUZHI.js";
8
+ import "./chunk-IS3AGU33.js";
9
+ import "./chunk-PTASB7SJ.js";
10
+ import "./chunk-XBEQI5H4.js";
11
11
  import "./chunk-HYRHI3QU.js";
12
12
  import "./chunk-M37RITA6.js";
13
13
  import "./chunk-AJ6ROGD7.js";
@@ -41,7 +41,7 @@ import {
41
41
  worktreeDisplayLabel,
42
42
  worktreeDisplayLabelForGroup,
43
43
  worktreeNameFromPath
44
- } from "./chunk-UFWEANLU.js";
44
+ } from "./chunk-XBEQI5H4.js";
45
45
  import "./chunk-HYRHI3QU.js";
46
46
  import "./chunk-M37RITA6.js";
47
47
  import "./chunk-AJ6ROGD7.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",