@sideboard-ai/core 0.1.231 → 0.1.233

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 (42) hide show
  1. package/dist/agents/cursor-runner.cjs +167 -62
  2. package/dist/agents/cursor-runner.js +16 -9
  3. package/dist/{agents-ENAQHQRZ.js → agents-J2CUJHPC.js} +16 -6
  4. package/dist/{agents-TCTEAK6M.js → agents-LDYMFW6N.js} +17 -7
  5. package/dist/{chunk-TQBUXKIB.js → chunk-26T43LBM.js} +97 -11
  6. package/dist/{chunk-PO4M4JAJ.js → chunk-2P4XKWV2.js} +1 -1
  7. package/dist/{chunk-ABOBTSO6.js → chunk-2UG25RMK.js} +18 -5
  8. package/dist/{chunk-4CZ25DO2.js → chunk-4VFOJZPS.js} +2 -2
  9. package/dist/{chunk-N5TX66CD.js → chunk-7H2CVVLF.js} +2 -2
  10. package/dist/{chunk-HZLZ34FK.js → chunk-BGHDWCAV.js} +69 -30
  11. package/dist/{chunk-SZJZEFTY.js → chunk-DD67JWTN.js} +1 -1
  12. package/dist/{chunk-JJRRLCCS.js → chunk-NLETJP7Z.js} +64 -30
  13. package/dist/{chunk-FA4CPYCY.js → chunk-PZQNE5X4.js} +81 -41
  14. package/dist/{chunk-HVUVQXZ2.js → chunk-QVN2WOKJ.js} +18 -5
  15. package/dist/{chunk-HZFLKVGC.js → chunk-S2OEFXST.js} +1 -1
  16. package/dist/{chunk-SXFRXNBM.js → chunk-SPAMHHJH.js} +1 -1
  17. package/dist/{chunk-ZXJMAKEP.js → chunk-TNVJPFHM.js} +5 -5
  18. package/dist/{chunk-UNTOZA3C.js → chunk-TWXSZYR2.js} +1 -1
  19. package/dist/{chunk-6L25K7BE.js → chunk-TXMFBL3X.js} +195 -73
  20. package/dist/{connected-teams-PIBQTOJU.js → connected-teams-JUJAQZZE.js} +2 -2
  21. package/dist/{connected-teams-RVD3PFAG.js → connected-teams-KQUDU7LZ.js} +2 -2
  22. package/dist/{coordinator-prompt-YKLXQTWH.js → coordinator-prompt-CE7MN6QY.js} +4 -4
  23. package/dist/{coordinator-prompt-DFXYTM6D.js → coordinator-prompt-IGDMFKCD.js} +4 -4
  24. package/dist/{global-workspace-EYZKYHAO.js → global-workspace-7WH24UFO.js} +4 -4
  25. package/dist/{global-workspace-4SPANNJT.js → global-workspace-NCIC2Y2U.js} +4 -4
  26. package/dist/index.cjs +542 -337
  27. package/dist/index.d.cts +32 -4
  28. package/dist/index.d.ts +32 -4
  29. package/dist/index.js +31 -8
  30. package/dist/mcp/run-stdio.cjs +465 -315
  31. package/dist/mcp/run-stdio.js +7 -7
  32. package/dist/{orchestrator-ZT3SJVKF.js → orchestrator-BXALNM4M.js} +8 -8
  33. package/dist/{orchestrator-HMJPNUN5.js → orchestrator-NLZMHTLX.js} +7 -7
  34. package/dist/{run-UEQM4JMZ.js → run-PCX5QWR4.js} +1 -1
  35. package/dist/{run-QDZCTP4Y.js → run-TB7RBII2.js} +1 -1
  36. package/dist/{workspaces-73Y7IJY2.js → workspaces-H2TRFDE2.js} +4 -4
  37. package/dist/{workspaces-FQCSCTNH.js → workspaces-NSQZSDQD.js} +4 -4
  38. package/dist/{worktree-DOPRKG7K.js → worktree-FMIX4BRV.js} +3 -3
  39. package/dist/{worktree-36VEFUJN.js → worktree-W7BSI4GU.js} +3 -3
  40. package/dist/{worktree-exclude-K7QK4RI5.js → worktree-exclude-5N2KTYIT.js} +2 -2
  41. package/dist/{worktree-exclude-L32WEBX6.js → worktree-exclude-RXZPTCNH.js} +2 -2
  42. package/package.json +2 -2
@@ -42,7 +42,7 @@ import {
42
42
  summarizeTurnStderr,
43
43
  toolDescription,
44
44
  turnFailChatText
45
- } from "./chunk-6L25K7BE.js";
45
+ } from "./chunk-TXMFBL3X.js";
46
46
  import {
47
47
  SLACK_REPLY_FORMATTING,
48
48
  addWorkspace,
@@ -65,7 +65,7 @@ import {
65
65
  stageAbsolutePathsAsAttachments,
66
66
  stageBuffersAsAttachments,
67
67
  syncWorkspacesFromThreads
68
- } from "./chunk-UNTOZA3C.js";
68
+ } from "./chunk-TWXSZYR2.js";
69
69
  import {
70
70
  addPrStackLayer,
71
71
  allocateTeamName,
@@ -114,7 +114,7 @@ import {
114
114
  worktreeDisplayLabelForGroup,
115
115
  worktreeNameFromPath,
116
116
  worktreeSlugForTicket
117
- } from "./chunk-4CZ25DO2.js";
117
+ } from "./chunk-4VFOJZPS.js";
118
118
  import {
119
119
  ATTACHMENTS_DIR,
120
120
  DETACHED_JOBS_DIR,
@@ -135,7 +135,7 @@ import {
135
135
  } from "./chunk-XJETIO4K.js";
136
136
  import {
137
137
  ensureWorktreeSideboardIgnored
138
- } from "./chunk-SZJZEFTY.js";
138
+ } from "./chunk-DD67JWTN.js";
139
139
  import {
140
140
  clearStaleIndexLocks,
141
141
  enrichPathWithNpmGlobalBin,
@@ -143,7 +143,7 @@ import {
143
143
  git,
144
144
  resolveGitDirsForLockRecovery,
145
145
  run
146
- } from "./chunk-ABOBTSO6.js";
146
+ } from "./chunk-2UG25RMK.js";
147
147
  import {
148
148
  HISTORY_CLEANUP_INTERVAL_HOURS_DEFAULT,
149
149
  applyTurnUsage,
@@ -418,7 +418,7 @@ async function continueSourceThread(threadId, prompt) {
418
418
  await continueOnReply(threadId, prompt);
419
419
  return;
420
420
  }
421
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-HMJPNUN5.js");
421
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-NLZMHTLX.js");
422
422
  await getOrchestrator2().send(threadId, prompt);
423
423
  } catch {
424
424
  }
@@ -896,9 +896,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
896
896
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
897
897
  );
898
898
  }
899
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-EYZKYHAO.js");
899
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-7WH24UFO.js");
900
900
  if (isGlobalThread2(thread)) {
901
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-YKLXQTWH.js");
901
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-CE7MN6QY.js");
902
902
  ensureGlobalCoordinatorCwd2(
903
903
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
904
904
  );
@@ -2185,7 +2185,17 @@ function pipeLines(stream, onLine) {
2185
2185
  const rl = createInterface2({ input: stream });
2186
2186
  rl.on("line", onLine);
2187
2187
  }
2188
+ function killableListenerPids(raw, selfPid = process.pid) {
2189
+ const pids = [];
2190
+ for (const token of raw.trim().split(/\s+/)) {
2191
+ const pid = Number(token);
2192
+ if (!Number.isFinite(pid) || pid <= 0 || pid === selfPid) continue;
2193
+ pids.push(pid);
2194
+ }
2195
+ return pids;
2196
+ }
2188
2197
  function killListenersOnPorts(ports) {
2198
+ const selfPid = process.pid;
2189
2199
  for (const port of ports) {
2190
2200
  if (!Number.isFinite(port) || port <= 0) continue;
2191
2201
  try {
@@ -2195,7 +2205,7 @@ function killListenersOnPorts(ports) {
2195
2205
  [
2196
2206
  "-NoProfile",
2197
2207
  "-Command",
2198
- `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
2208
+ `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | Where-Object { $_.OwningProcess -ne ${selfPid} } | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
2199
2209
  ],
2200
2210
  { stdio: "ignore", timeout: 3e3 }
2201
2211
  );
@@ -2211,9 +2221,7 @@ function killListenersOnPorts(ports) {
2211
2221
  } catch {
2212
2222
  continue;
2213
2223
  }
2214
- for (const token of raw.trim().split(/\s+/)) {
2215
- const pid = Number(token);
2216
- if (!Number.isFinite(pid) || pid <= 0) continue;
2224
+ for (const pid of killableListenerPids(raw, selfPid)) {
2217
2225
  try {
2218
2226
  process.kill(pid, "SIGTERM");
2219
2227
  } catch {
@@ -2266,6 +2274,9 @@ async function spawnWorkspaceScript(command, opts) {
2266
2274
  );
2267
2275
  } catch {
2268
2276
  }
2277
+ if (opts.heldPorts?.length) {
2278
+ await Promise.all(opts.heldPorts.map((h) => h.release()));
2279
+ }
2269
2280
  const shell = process.platform === "darwin" ? "zsh" : "bash";
2270
2281
  const child = execa2(shell, ["-lc", command], {
2271
2282
  cwd: opts.worktreePath,
@@ -2446,11 +2457,11 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2446
2457
  const held = await reservePortRange(PORT_RANGE_SIZE);
2447
2458
  const ports = held.map((h) => h.port);
2448
2459
  try {
2449
- await Promise.all(held.map((h) => h.release()));
2450
2460
  const handle = await spawnWorkspaceScript(script.command, {
2451
2461
  worktreePath,
2452
2462
  repoPath,
2453
2463
  ports,
2464
+ heldPorts: held,
2454
2465
  defaultBranch: opts?.defaultBranch,
2455
2466
  onLine
2456
2467
  });
@@ -2897,8 +2908,8 @@ async function cloneRepoIntoSideboard(opts) {
2897
2908
  }
2898
2909
 
2899
2910
  // src/types/thread.ts
2900
- function isUnclaimedRunScriptRequest(req) {
2901
- return Boolean(req && !req.claimedAt && !req.fulfilledAt && !req.error);
2911
+ function isAdoptableRunScriptRequest(req) {
2912
+ return Boolean(req && !req.fulfilledAt && !req.error);
2902
2913
  }
2903
2914
 
2904
2915
  // src/store/desktop-host.ts
@@ -4530,7 +4541,7 @@ async function adoptThread(input) {
4530
4541
  throw new Error(`Worktree not found: ${input.worktreePath}`);
4531
4542
  }
4532
4543
  const repoPath = await resolveRepoRoot(input.worktreePath);
4533
- const { stdout: branch } = await import("./run-QDZCTP4Y.js").then(
4544
+ const { stdout: branch } = await import("./run-TB7RBII2.js").then(
4534
4545
  ({ git: git2 }) => git2(["rev-parse", "--abbrev-ref", "HEAD"], input.worktreePath)
4535
4546
  );
4536
4547
  const thread = createEmptyThread({
@@ -4545,9 +4556,9 @@ async function adoptThread(input) {
4545
4556
  messages: input.messages ?? []
4546
4557
  });
4547
4558
  writeThread(thread);
4548
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FQCSCTNH.js");
4559
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-NSQZSDQD.js");
4549
4560
  await ensureWorkspace2(repoPath);
4550
- const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-K7QK4RI5.js");
4561
+ const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-5N2KTYIT.js");
4551
4562
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
4552
4563
  return thread;
4553
4564
  }
@@ -5950,7 +5961,7 @@ async function confirmLand(thread, opts) {
5950
5961
  if (preview.dirty) {
5951
5962
  committed = await commitAll(thread.worktreePath, meta.commitMessage);
5952
5963
  }
5953
- const { git: git2 } = await import("./run-QDZCTP4Y.js");
5964
+ const { git: git2 } = await import("./run-TB7RBII2.js");
5954
5965
  const { stdout: headOut } = await git2(
5955
5966
  ["rev-parse", "--abbrev-ref", "HEAD"],
5956
5967
  thread.worktreePath,
@@ -7021,7 +7032,7 @@ function formatScheduledPrompt(name, prompt) {
7021
7032
  ${prompt}`;
7022
7033
  }
7023
7034
  async function defaultDeps() {
7024
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-HMJPNUN5.js");
7035
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-NLZMHTLX.js");
7025
7036
  const orch = getOrchestrator2();
7026
7037
  return {
7027
7038
  findThread: (id) => findThreadByRef(id),
@@ -7205,6 +7216,8 @@ var Orchestrator = class {
7205
7216
  jobContinueNudged = /* @__PURE__ */ new Set();
7206
7217
  /** In-flight startDev per worktree run key — agent MCP + UI Start must not double-spawn. */
7207
7218
  startingDev = /* @__PURE__ */ new Map();
7219
+ /** Serialize desktop apply of `runScriptRequest` per thread (start then stop). */
7220
+ applyingRunScriptThreads = /* @__PURE__ */ new Set();
7208
7221
  /**
7209
7222
  * MCP/CLI poll budget while Electron main adopts `runScriptRequest`.
7210
7223
  * Tests shorten this so a missing desktop does not wait 20s.
@@ -7391,11 +7404,19 @@ var Orchestrator = class {
7391
7404
  for (const thread of listThreads()) {
7392
7405
  if (thread.status === "archived") continue;
7393
7406
  const req = thread.runScriptRequest;
7394
- if (!isUnclaimedRunScriptRequest(req) || !req) continue;
7395
- const claimedAt = (/* @__PURE__ */ new Date()).toISOString();
7407
+ if (!isAdoptableRunScriptRequest(req) || !req) continue;
7408
+ if (this.applyingRunScriptThreads.has(thread.id)) continue;
7409
+ const claimedAt = req.claimedAt ?? (/* @__PURE__ */ new Date()).toISOString();
7396
7410
  const claimed = { ...req, claimedAt };
7411
+ this.applyingRunScriptThreads.add(thread.id);
7397
7412
  updateThread(thread.id, { runScriptRequest: claimed });
7398
- void this.applyRunScriptRequest(thread.id, claimed);
7413
+ void this.applyRunScriptRequest(thread.id, claimed).finally(() => {
7414
+ this.applyingRunScriptThreads.delete(thread.id);
7415
+ const latest = readThread(thread.id)?.runScriptRequest;
7416
+ if (latest && latest.requestId !== req.requestId && isAdoptableRunScriptRequest(latest)) {
7417
+ this.adoptPersistedRunScripts();
7418
+ }
7419
+ });
7399
7420
  }
7400
7421
  }
7401
7422
  async applyRunScriptRequest(threadId, req) {
@@ -8651,9 +8672,8 @@ var Orchestrator = class {
8651
8672
  if (fulfilled && !run2) {
8652
8673
  throw new Error("Desktop reported the run script started, but it is not active.");
8653
8674
  }
8654
- } else {
8655
- const gone = scriptName ? !run2 : shared.activeRuns.length === 0;
8656
- if (fulfilled || gone) return null;
8675
+ } else if (fulfilled) {
8676
+ return null;
8657
8677
  }
8658
8678
  await new Promise((r) => setTimeout(r, 40));
8659
8679
  }
@@ -8661,12 +8681,26 @@ var Orchestrator = class {
8661
8681
  op === "start" ? "Desktop did not start the run script in time. Is Sideboard.app running this worktree?" : "Desktop did not stop the run script in time."
8662
8682
  );
8663
8683
  }
8684
+ async awaitStartingDev(worktreePath, scriptName) {
8685
+ const keys = scriptName ? [worktreeRunProcessKey(worktreePath, scriptName)] : [...this.startingDev.keys()].filter((k) => isWorktreeRunProcessKey(k, worktreePath));
8686
+ await Promise.all(
8687
+ keys.map(async (key) => {
8688
+ const pending = this.startingDev.get(key);
8689
+ if (!pending) return;
8690
+ try {
8691
+ await pending;
8692
+ } catch {
8693
+ }
8694
+ })
8695
+ );
8696
+ }
8664
8697
  async stopDev(threadRef, scriptName) {
8665
8698
  const thread = this.requireThread(threadRef);
8666
8699
  if (!this.shouldOwnRunScripts()) {
8667
8700
  await this.requestDesktopRunScript(thread, "stop", scriptName);
8668
8701
  return;
8669
8702
  }
8703
+ await this.awaitStartingDev(thread.worktreePath, scriptName);
8670
8704
  const siblings = threadsSharingWorktree(thread.worktreePath);
8671
8705
  const shared = mergeWorktreeActiveRuns(siblings);
8672
8706
  if (scriptName) {
@@ -9529,7 +9563,7 @@ var Orchestrator = class {
9529
9563
  this.enforceHistoryRetention();
9530
9564
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
9531
9565
  try {
9532
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-FQCSCTNH.js");
9566
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-NSQZSDQD.js");
9533
9567
  await ensureWorkspace2(thread.repoPath);
9534
9568
  } catch {
9535
9569
  }
@@ -9583,13 +9617,13 @@ var Orchestrator = class {
9583
9617
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
9584
9618
  );
9585
9619
  }
9586
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-DOPRKG7K.js");
9620
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-FMIX4BRV.js");
9587
9621
  const slug = thread.worktreePath.split("/").pop();
9588
9622
  const dest = thread.worktreePath;
9589
9623
  await withRepoGitLock(thread.repoPath, async () => {
9590
9624
  await git(["worktree", "add", dest, thread.branchName], thread.repoPath);
9591
9625
  });
9592
- const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-K7QK4RI5.js");
9626
+ const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-5N2KTYIT.js");
9593
9627
  await ensureWorktreeSideboardIgnored2(dest);
9594
9628
  void createThreadWorktree2;
9595
9629
  void slug;
@@ -9693,7 +9727,7 @@ async function startOrchestration(opts) {
9693
9727
  sourceRef: "default",
9694
9728
  ...createOpts
9695
9729
  }).catch(async () => {
9696
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-DOPRKG7K.js");
9730
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-FMIX4BRV.js");
9697
9731
  const repo = await resolveRepoRoot2(repoPath);
9698
9732
  const def = await resolveDefaultBranch2(repo);
9699
9733
  return orch.createThread({
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  isOrchestratorThread
3
- } from "./chunk-HZFLKVGC.js";
3
+ } from "./chunk-S2OEFXST.js";
4
4
  import {
5
5
  codexUnattendedGitConfigArgs,
6
6
  mergeAgentGitAuthEnv,
7
7
  resolveAgentGitAuthEnv,
8
8
  resolveCodexGitWritableRoots
9
- } from "./chunk-N5TX66CD.js";
9
+ } from "./chunk-7H2CVVLF.js";
10
10
  import {
11
11
  applyConnectedTeamToCli,
12
12
  brightsyMcpServerName,
13
13
  ensureCliTeamTracked,
14
14
  ensureConnectedBrightsyTeamTokens
15
- } from "./chunk-ZXJMAKEP.js";
15
+ } from "./chunk-TNVJPFHM.js";
16
16
  import {
17
17
  loadBrightsyConfig
18
18
  } from "./chunk-ROPGQRPU.js";
@@ -20,20 +20,23 @@ import {
20
20
  httpFetch
21
21
  } from "./chunk-N7WZ6NIN.js";
22
22
  import {
23
+ CURSOR_SDK_INSTALL_HINT,
23
24
  applyAgentRunnerHeapEnv,
24
25
  applyNodeLaunch,
25
26
  clipToolResultForStore,
26
27
  cursorRipgrepEnv,
27
28
  extractJsonErrorMessage,
28
29
  formatUnknownDetail,
30
+ importCursorSdk,
29
31
  isAsarPath,
32
+ isCursorSdkInstalled,
30
33
  looksLikeAgentFailureMessage,
31
34
  looksLikeBrightsyEmptyCompletion,
32
35
  packagedCursorRunnerPath,
33
36
  packagedMcpStdioPath,
34
37
  parseCursorRunnerLine,
35
38
  resolveNodeLaunch
36
- } from "./chunk-TQBUXKIB.js";
39
+ } from "./chunk-26T43LBM.js";
37
40
  import {
38
41
  claudeChromeEnabled,
39
42
  fromInclusiveInputUsage,
@@ -52,7 +55,7 @@ import {
52
55
  resolveCommandBinarySync,
53
56
  run,
54
57
  withExportedPath
55
- } from "./chunk-HVUVQXZ2.js";
58
+ } from "./chunk-QVN2WOKJ.js";
56
59
  import {
57
60
  isElectronLikeCommand,
58
61
  unwrapStrippedElectronLaunch
@@ -2117,7 +2120,7 @@ var claudeAdapter = {
2117
2120
  );
2118
2121
  }
2119
2122
  const mode = permissionMode(thread);
2120
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-4SPANNJT.js");
2123
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-NCIC2Y2U.js");
2121
2124
  const isOrchestrator = isOrchestratorThread2(thread);
2122
2125
  const injectedServers = await buildInjectedMcpServers({
2123
2126
  includeSideboard: true,
@@ -2734,7 +2737,6 @@ import { existsSync as existsSync7 } from "fs";
2734
2737
  import { createRequire as createRequire2 } from "module";
2735
2738
  import { dirname as dirname3, join as join5 } from "path";
2736
2739
  import { fileURLToPath as fileURLToPath2 } from "url";
2737
- import { Cursor } from "@cursor/sdk";
2738
2740
  var FALLBACK_CURSOR_MODELS = [
2739
2741
  { id: "default", displayName: "Auto" },
2740
2742
  { id: "composer-2.5", displayName: "Composer 2.5" },
@@ -2765,8 +2767,10 @@ async function listCursorModels() {
2765
2767
  }
2766
2768
  const apiKey = resolveCursorApiKey();
2767
2769
  if (!apiKey) return FALLBACK_CURSOR_MODELS;
2770
+ const sdk = await importCursorSdk();
2771
+ if (!sdk) return FALLBACK_CURSOR_MODELS;
2768
2772
  try {
2769
- const listed = await Cursor.models.list({ apiKey });
2773
+ const listed = await sdk.Cursor.models.list({ apiKey });
2770
2774
  const models = listed.map((m) => ({
2771
2775
  id: m.id,
2772
2776
  displayName: m.displayName || m.id,
@@ -2815,6 +2819,16 @@ function cursorRunnerPath() {
2815
2819
  var cursorAdapter = {
2816
2820
  kind: "cursor",
2817
2821
  async detect() {
2822
+ if (!isCursorSdkInstalled()) {
2823
+ return {
2824
+ agent: "cursor",
2825
+ installed: false,
2826
+ authenticated: false,
2827
+ linearMcp: false,
2828
+ warnings: [],
2829
+ reason: CURSOR_SDK_INSTALL_HINT
2830
+ };
2831
+ }
2818
2832
  const apiKey = resolveCursorApiKey();
2819
2833
  if (!apiKey) {
2820
2834
  return {
@@ -3810,6 +3824,7 @@ async function getClaudePlanUsage(opts = {}) {
3810
3824
  }
3811
3825
 
3812
3826
  // src/agents/install.ts
3827
+ import { existsSync as existsSync10 } from "fs";
3813
3828
  var SETUP = {
3814
3829
  claude: {
3815
3830
  agent: "claude",
@@ -3818,7 +3833,8 @@ var SETUP = {
3818
3833
  docsUrl: "https://code.claude.com/docs/en/install",
3819
3834
  installCommand: "npm install -g @anthropic-ai/claude-code",
3820
3835
  loginCommand: "claude auth login",
3821
- npmPackage: "@anthropic-ai/claude-code"
3836
+ npmPackage: "@anthropic-ai/claude-code",
3837
+ updateArgs: ["update"]
3822
3838
  },
3823
3839
  codex: {
3824
3840
  agent: "codex",
@@ -3827,7 +3843,8 @@ var SETUP = {
3827
3843
  docsUrl: "https://github.com/openai/codex",
3828
3844
  installCommand: "npm install -g @openai/codex",
3829
3845
  loginCommand: "codex login",
3830
- npmPackage: "@openai/codex"
3846
+ npmPackage: "@openai/codex",
3847
+ updateArgs: ["update"]
3831
3848
  },
3832
3849
  opencode: {
3833
3850
  agent: "opencode",
@@ -3837,20 +3854,22 @@ var SETUP = {
3837
3854
  // Prefer the official installer; npm package name is opencode-ai.
3838
3855
  installCommand: "curl -fsSL https://opencode.ai/install | bash",
3839
3856
  loginCommand: "opencode auth login",
3840
- npmPackage: "opencode-ai@latest"
3857
+ npmPackage: "opencode-ai@latest",
3858
+ updateArgs: ["upgrade"]
3841
3859
  },
3842
3860
  cursor: {
3843
3861
  agent: "cursor",
3844
- kind: "bundled-sdk",
3845
- summary: "No CLI install \u2014 Sideboard ships the Cursor SDK. Add a CURSOR_API_KEY from the Cursor dashboard.",
3846
- docsUrl: "https://cursor.com/dashboard/integrations",
3847
- installCommand: null,
3848
- loginCommand: null
3862
+ kind: "cli",
3863
+ summary: "Install (or update) @cursor/sdk with npm \u2014 Sideboard does not pin it in the app. Then add a CURSOR_API_KEY.",
3864
+ docsUrl: "https://www.npmjs.com/package/@cursor/sdk",
3865
+ installCommand: "npm install -g @cursor/sdk",
3866
+ loginCommand: null,
3867
+ npmPackage: "@cursor/sdk"
3849
3868
  },
3850
3869
  brightsy: {
3851
3870
  agent: "brightsy",
3852
3871
  kind: "cli",
3853
- summary: "Install the Brightsy CLI, then run `brightsy login`.",
3872
+ summary: "Install (or update) the Brightsy CLI with npm, then run `brightsy login`.",
3854
3873
  docsUrl: "https://www.npmjs.com/package/@brightsy/cli",
3855
3874
  installCommand: "npm install -g @brightsy/cli",
3856
3875
  loginCommand: "brightsy login",
@@ -3946,16 +3965,6 @@ async function whichOnPath(bin) {
3946
3965
  }
3947
3966
  async function installNpmGlobalPackage(opts) {
3948
3967
  enrichPathWithNpmGlobalBin();
3949
- if (opts.cliBin) {
3950
- const existing = await whichOnPath(opts.cliBin);
3951
- if (existing) {
3952
- return {
3953
- ok: true,
3954
- command: existing,
3955
- message: isConductorBundledCli(existing) ? `Using Conductor\u2019s ${opts.cliBin} at ${existing}. No extra install \u2014 Log in only if auth is missing.` : `Already on PATH: ${existing}`
3956
- };
3957
- }
3958
- }
3959
3968
  const result = await run("npm", ["install", "-g", opts.npmPackage], { reject: false });
3960
3969
  const ok = result.exitCode === 0;
3961
3970
  if (!ok) {
@@ -3992,6 +4001,33 @@ async function installNpmGlobalPackage(opts) {
3992
4001
  message: binPath ? `Installed ${opts.npmPackage} \u2192 ${binPath}` : `Installed ${opts.npmPackage} (CLI not yet on PATH)`
3993
4002
  };
3994
4003
  }
4004
+ async function resolveExistingCli(agent) {
4005
+ if (agent === "cursor") return null;
4006
+ const exe = resolveAgentExecutable(agent);
4007
+ if ((exe.includes("/") || exe.includes("\\")) && existsSync10(exe)) return exe;
4008
+ enrichPathWithNpmGlobalBin();
4009
+ return whichOnPath(CLI_BIN[agent]);
4010
+ }
4011
+ async function runVendorUpdate(existing, updateArgs) {
4012
+ const result = await run(existing, updateArgs, {
4013
+ reject: false,
4014
+ timeoutMs: 12e4
4015
+ });
4016
+ const command = `${existing} ${updateArgs.join(" ")}`;
4017
+ const ok = result.exitCode === 0;
4018
+ const detail = result.stdout.trim() || result.stderr.trim() || `exit ${result.exitCode}`;
4019
+ return {
4020
+ ok,
4021
+ command,
4022
+ exitCode: result.exitCode,
4023
+ stdout: result.stdout,
4024
+ stderr: result.stderr,
4025
+ message: ok ? isConductorBundledCli(existing) ? `Updated Conductor\u2019s CLI at ${existing}. ${detail}` : `Updated ${command}. ${detail}` : `Update failed (${command}): ${detail}`
4026
+ };
4027
+ }
4028
+ function isCurlInstaller(command) {
4029
+ return /curl\s| \|\s*bash/.test(command);
4030
+ }
3995
4031
  async function installAgent(agent) {
3996
4032
  const info = getAgentSetupInfo(agent);
3997
4033
  if (info.kind === "bundled-sdk" || info.kind === "api-key") {
@@ -4003,21 +4039,25 @@ async function installAgent(agent) {
4003
4039
  if (!info.installCommand) {
4004
4040
  return { ok: false, message: `No install command for ${agent}` };
4005
4041
  }
4006
- if (/curl\s| \|\s*bash/.test(info.installCommand) || !info.npmPackage) {
4007
- await openInSystemTerminal(info.installCommand);
4008
- return {
4009
- ok: true,
4010
- openedTerminal: true,
4011
- command: info.installCommand,
4012
- message: `Opened Terminal to run: ${info.installCommand}`
4013
- };
4042
+ const existing = await resolveExistingCli(agent);
4043
+ if (existing && info.updateArgs?.length) {
4044
+ return runVendorUpdate(existing, info.updateArgs);
4014
4045
  }
4015
- const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
4016
- return installNpmGlobalPackage({
4017
- npmPackage: info.npmPackage,
4018
- installCommand: info.installCommand,
4019
- cliBin
4020
- });
4046
+ if (info.npmPackage && !isCurlInstaller(info.installCommand)) {
4047
+ const cliBin = agent === "cursor" ? null : CLI_BIN[agent];
4048
+ return installNpmGlobalPackage({
4049
+ npmPackage: info.npmPackage,
4050
+ installCommand: info.installCommand,
4051
+ cliBin
4052
+ });
4053
+ }
4054
+ await openInSystemTerminal(info.installCommand);
4055
+ return {
4056
+ ok: true,
4057
+ openedTerminal: true,
4058
+ command: info.installCommand,
4059
+ message: `Opened Terminal to run: ${info.installCommand}`
4060
+ };
4021
4061
  }
4022
4062
  async function loginAgent(agent) {
4023
4063
  const info = getAgentSetupInfo(agent);
@@ -63,6 +63,14 @@ function ensureAgentPath(env = process.env) {
63
63
  }
64
64
  function enrichPathWithNpmGlobalBin(env = process.env) {
65
65
  ensureAgentPath(env);
66
+ const prefix = npmGlobalPrefix(env);
67
+ if (prefix) {
68
+ const binDir = process.platform === "win32" ? prefix : join(prefix, "bin");
69
+ prependPathDir(env, binDir);
70
+ }
71
+ return env.PATH ?? "";
72
+ }
73
+ function npmGlobalPrefix(env = process.env) {
66
74
  try {
67
75
  const prefix = execFileSync("npm", ["prefix", "-g"], {
68
76
  encoding: "utf8",
@@ -70,13 +78,16 @@ function enrichPathWithNpmGlobalBin(env = process.env) {
70
78
  timeout: 8e3,
71
79
  stdio: ["ignore", "pipe", "ignore"]
72
80
  }).trim().split(/\r?\n/).find(Boolean);
73
- if (prefix) {
74
- const binDir = process.platform === "win32" ? prefix : join(prefix, "bin");
75
- prependPathDir(env, binDir);
76
- }
81
+ return prefix || null;
77
82
  } catch {
83
+ return null;
78
84
  }
79
- return env.PATH ?? "";
85
+ }
86
+ function npmGlobalNodeModules(env = process.env) {
87
+ const prefix = npmGlobalPrefix(env);
88
+ if (!prefix) return null;
89
+ const dir = process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
90
+ return existsSync(dir) ? dir : null;
80
91
  }
81
92
  function posixShellSingleQuote(value) {
82
93
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -307,6 +318,8 @@ export {
307
318
  isConductorBundledCli,
308
319
  ensureAgentPath,
309
320
  enrichPathWithNpmGlobalBin,
321
+ npmGlobalPrefix,
322
+ npmGlobalNodeModules,
310
323
  posixShellSingleQuote,
311
324
  resolveCommandBinarySync,
312
325
  withExportedPath,
@@ -6,7 +6,7 @@ import {
6
6
  resolveRepoRoot,
7
7
  takenSlugsFromThread,
8
8
  teamSlugFromName
9
- } from "./chunk-N5TX66CD.js";
9
+ } from "./chunk-7H2CVVLF.js";
10
10
  import {
11
11
  ATTACHMENTS_DIR,
12
12
  attachmentsGitignoreBody
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-P2BSMNHB.js";
11
11
  import {
12
12
  run
13
- } from "./chunk-ABOBTSO6.js";
13
+ } from "./chunk-2UG25RMK.js";
14
14
  import {
15
15
  appDataDir
16
16
  } from "./chunk-EQNRWDLN.js";
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-WFUDY6PC.js";
12
12
  import {
13
13
  run
14
- } from "./chunk-HVUVQXZ2.js";
14
+ } from "./chunk-QVN2WOKJ.js";
15
15
 
16
16
  // src/brightsy/connected-teams.ts
17
17
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -19,7 +19,7 @@ import { join } from "path";
19
19
 
20
20
  // src/brightsy/accounts.ts
21
21
  async function loadConnectedTeams() {
22
- const { listConnectedBrightsyTeams: listConnectedBrightsyTeams2 } = await import("./connected-teams-PIBQTOJU.js");
22
+ const { listConnectedBrightsyTeams: listConnectedBrightsyTeams2 } = await import("./connected-teams-JUJAQZZE.js");
23
23
  return listConnectedBrightsyTeams2();
24
24
  }
25
25
  async function runBrightsyTeamsJson(args) {
@@ -48,14 +48,14 @@ async function listBrightsyAccounts() {
48
48
  async function getBrightsySession() {
49
49
  try {
50
50
  const { ensureBrightsyLocalConfigFresh: ensureBrightsyLocalConfigFresh2 } = await import("./oauth-S3M7B475.js");
51
- const { ensureConnectedBrightsyTeamTokens: ensureConnectedBrightsyTeamTokens2 } = await import("./connected-teams-PIBQTOJU.js");
51
+ const { ensureConnectedBrightsyTeamTokens: ensureConnectedBrightsyTeamTokens2 } = await import("./connected-teams-JUJAQZZE.js");
52
52
  await ensureBrightsyLocalConfigFresh2();
53
53
  await ensureConnectedBrightsyTeamTokens2();
54
54
  loadBrightsyConfig();
55
55
  const data = await runBrightsyTeamsJson([]);
56
56
  const accounts = Array.isArray(data.teams) ? data.teams : [];
57
57
  const active = data.active ?? accounts.find((a) => a.active) ?? null;
58
- const { ensureCliTeamTracked: ensureCliTeamTracked2 } = await import("./connected-teams-PIBQTOJU.js");
58
+ const { ensureCliTeamTracked: ensureCliTeamTracked2 } = await import("./connected-teams-JUJAQZZE.js");
59
59
  const connectedTeams = ensureCliTeamTracked2(
60
60
  active ? { id: active.id, slug: active.slug, name: active.name } : void 0
61
61
  );
@@ -95,7 +95,7 @@ async function performBrightsyAccountSwitch(accountIdOrSlug) {
95
95
  return { cfg, target: data.active };
96
96
  }
97
97
  async function switchBrightsyAccount(accountIdOrSlug) {
98
- const { connectBrightsyTeam: connectBrightsyTeam2 } = await import("./connected-teams-PIBQTOJU.js");
98
+ const { connectBrightsyTeam: connectBrightsyTeam2 } = await import("./connected-teams-JUJAQZZE.js");
99
99
  await connectBrightsyTeam2(accountIdOrSlug);
100
100
  return getBrightsySession();
101
101
  }
@@ -8,7 +8,7 @@ import {
8
8
  resolveRepoRoot,
9
9
  takenSlugsFromThread,
10
10
  teamSlugFromName
11
- } from "./chunk-4CZ25DO2.js";
11
+ } from "./chunk-4VFOJZPS.js";
12
12
  import {
13
13
  ATTACHMENTS_DIR,
14
14
  attachmentsGitignoreBody