@sideboard-ai/core 0.1.227 → 0.1.229

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 (44) hide show
  1. package/dist/{abletime-D6773P33.js → abletime-BGVOCEOU.js} +4 -4
  2. package/dist/{abletime-BF253OZT.js → abletime-WZNDTRVL.js} +4 -4
  3. package/dist/{abletime-oauth-KQPVVBKA.js → abletime-oauth-6M5GWEPX.js} +3 -3
  4. package/dist/{abletime-oauth-H4672PRH.js → abletime-oauth-ZLU2EYRK.js} +3 -3
  5. package/dist/{abletime-rest-DAFPXZ7H.js → abletime-rest-F7XG5NGY.js} +3 -3
  6. package/dist/{abletime-rest-OPEVV3XC.js → abletime-rest-WNHIATB3.js} +3 -3
  7. package/dist/{agents-DV5UROWB.js → agents-B7G4BQTS.js} +4 -4
  8. package/dist/{agents-QQ77D7VT.js → agents-ZKP7QNXA.js} +4 -4
  9. package/dist/{app-settings-5RQMQKSB.js → app-settings-KBL2G7M4.js} +7 -1
  10. package/dist/{app-settings-V2BBCX2H.js → app-settings-UXWCPDEA.js} +7 -1
  11. package/dist/{chunk-FUDJ6NFM.js → chunk-6HKGCUDN.js} +117 -62
  12. package/dist/{chunk-N2VST3NL.js → chunk-7YKXBFQE.js} +2 -2
  13. package/dist/{chunk-KTZESHYH.js → chunk-AMMRFFB7.js} +1 -1
  14. package/dist/{chunk-HSYE5ID6.js → chunk-AS4UN7QL.js} +5 -5
  15. package/dist/{chunk-3NWUHXU3.js → chunk-C774RJJA.js} +3 -3
  16. package/dist/{chunk-NFWPDEN4.js → chunk-F3GT6DQW.js} +1 -1
  17. package/dist/{chunk-CLI5DVU4.js → chunk-GHRZNPBK.js} +2 -2
  18. package/dist/{chunk-EWHJNJHV.js → chunk-I5NC7DWO.js} +38 -0
  19. package/dist/{chunk-VUW3VKLW.js → chunk-LULHFLPU.js} +17 -4
  20. package/dist/{chunk-646I442S.js → chunk-MVLXDYN5.js} +5 -5
  21. package/dist/{chunk-P2EAAM3H.js → chunk-N7UXGM3Y.js} +2 -2
  22. package/dist/{chunk-RRW2XWAY.js → chunk-P6ZM5QJW.js} +2 -2
  23. package/dist/{chunk-THLVCXLF.js → chunk-RGNPPRZZ.js} +3 -3
  24. package/dist/{chunk-Y5JES2VV.js → chunk-RZTRWN2V.js} +38 -0
  25. package/dist/{chunk-SOGGKYFV.js → chunk-SWNEA5OZ.js} +3 -3
  26. package/dist/{chunk-SQHRPJ7P.js → chunk-T7Y5ULHM.js} +14 -4
  27. package/dist/{chunk-I2HOATRJ.js → chunk-UHT6OS7V.js} +129 -62
  28. package/dist/{coordinator-prompt-JLMQVORO.js → coordinator-prompt-DZMMCDRW.js} +3 -3
  29. package/dist/{coordinator-prompt-VZ7OSRLD.js → coordinator-prompt-RL3GKIAQ.js} +3 -3
  30. package/dist/{global-workspace-QPSJEXGR.js → global-workspace-G3QEVRGZ.js} +3 -3
  31. package/dist/{global-workspace-3FDAUNN2.js → global-workspace-QW4OXCLJ.js} +3 -3
  32. package/dist/index.cjs +927 -658
  33. package/dist/index.d.cts +89 -3
  34. package/dist/index.d.ts +89 -3
  35. package/dist/index.js +206 -67
  36. package/dist/mcp/run-stdio.cjs +224 -84
  37. package/dist/mcp/run-stdio.js +86 -50
  38. package/dist/{orchestrator-CWOSAYAE.js → orchestrator-GKAHLGXT.js} +5 -5
  39. package/dist/{orchestrator-VKPHI64Q.js → orchestrator-X4VFA5WW.js} +5 -5
  40. package/dist/{workspaces-76W7IEKR.js → workspaces-77C5XQEY.js} +3 -3
  41. package/dist/{workspaces-TDBA64II.js → workspaces-WXRPLT2N.js} +3 -3
  42. package/dist/{worktree-I573UOPD.js → worktree-L6FJXFUC.js} +2 -2
  43. package/dist/{worktree-7V67XTEO.js → worktree-P67WAJKH.js} +2 -2
  44. package/package.json +1 -1
@@ -3787,12 +3787,14 @@ var init_issue_source_labels = __esm({
3787
3787
  // src/store/app-settings.ts
3788
3788
  var app_settings_exports = {};
3789
3789
  __export(app_settings_exports, {
3790
+ AGENT_DONE_SOUNDS: () => AGENT_DONE_SOUNDS,
3790
3791
  FOLLOW_UP_BEHAVIORS: () => FOLLOW_UP_BEHAVIORS,
3791
3792
  GITHUB_GIT_AUTH_MODES: () => GITHUB_GIT_AUTH_MODES,
3792
3793
  HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
3793
3794
  ISSUE_SOURCE_LABELS: () => ISSUE_SOURCE_LABELS,
3794
3795
  PROFILE_NOTES_MAX: () => PROFILE_NOTES_MAX,
3795
3796
  USAGE_ON_LIMIT_VALUES: () => USAGE_ON_LIMIT_VALUES,
3797
+ agentDoneSound: () => agentDoneSound,
3796
3798
  appSettingsPath: () => appSettingsPath,
3797
3799
  applyAppEnvironment: () => applyAppEnvironment,
3798
3800
  autoArchiveOnMergeEnabled: () => autoArchiveOnMergeEnabled,
@@ -3840,6 +3842,7 @@ __export(app_settings_exports, {
3840
3842
  historyMaxCount: () => historyMaxCount,
3841
3843
  historyMaxDays: () => historyMaxDays,
3842
3844
  isAbleTimeConnected: () => isAbleTimeConnected,
3845
+ isAgentDoneSound: () => isAgentDoneSound,
3843
3846
  isIssueSourceConnected: () => isIssueSourceConnected,
3844
3847
  isLinearConnected: () => isLinearConnected,
3845
3848
  isUsageOnLimit: () => isUsageOnLimit,
@@ -3881,6 +3884,9 @@ __export(app_settings_exports, {
3881
3884
  usageOnLimit: () => usageOnLimit,
3882
3885
  writeViewerContext: () => writeViewerContext
3883
3886
  });
3887
+ function isAgentDoneSound(value) {
3888
+ return AGENT_DONE_SOUNDS.includes(value);
3889
+ }
3884
3890
  function emptyPublicIntegrations() {
3885
3891
  return {
3886
3892
  hasLinearApiKey: false,
@@ -4311,6 +4317,13 @@ function normalizeAdvanced(raw) {
4311
4317
  if (source.followUpBehavior === "queue" || source.followUpBehavior === "steer") {
4312
4318
  out.followUpBehavior = source.followUpBehavior;
4313
4319
  }
4320
+ if (isAgentDoneSound(source.agentDoneSound)) {
4321
+ out.agentDoneSound = source.agentDoneSound;
4322
+ }
4323
+ if (typeof source.agentDoneCustomSoundName === "string") {
4324
+ const name = source.agentDoneCustomSoundName.trim();
4325
+ if (name) out.agentDoneCustomSoundName = name.slice(0, 200);
4326
+ }
4314
4327
  return out;
4315
4328
  }
4316
4329
  function normalizeSettings(raw) {
@@ -5221,6 +5234,16 @@ function updateAdvancedSettings(patch) {
5221
5234
  if (patch.followUpBehavior === "queue" || patch.followUpBehavior === "steer") {
5222
5235
  advanced.followUpBehavior = patch.followUpBehavior;
5223
5236
  }
5237
+ if (isAgentDoneSound(patch.agentDoneSound)) {
5238
+ advanced.agentDoneSound = patch.agentDoneSound;
5239
+ }
5240
+ if (patch.agentDoneCustomSoundName === null || patch.agentDoneCustomSoundName === "") {
5241
+ delete advanced.agentDoneCustomSoundName;
5242
+ } else if (typeof patch.agentDoneCustomSoundName === "string") {
5243
+ const name = patch.agentDoneCustomSoundName.trim();
5244
+ if (name) advanced.agentDoneCustomSoundName = name.slice(0, 200);
5245
+ else delete advanced.agentDoneCustomSoundName;
5246
+ }
5224
5247
  return saveAppSettings({ ...current, advanced });
5225
5248
  }
5226
5249
  function autoRenameBranchEnabled(settings = loadAppSettings()) {
@@ -5288,6 +5311,9 @@ function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
5288
5311
  function followUpBehavior(settings = loadAppSettings()) {
5289
5312
  return settings.advanced.followUpBehavior === "queue" ? "queue" : "steer";
5290
5313
  }
5314
+ function agentDoneSound(settings = loadAppSettings()) {
5315
+ return isAgentDoneSound(settings.advanced.agentDoneSound) ? settings.advanced.agentDoneSound : "none";
5316
+ }
5291
5317
  function maxConcurrentAgents(settings = loadAppSettings()) {
5292
5318
  const n = settings.advanced.maxConcurrent;
5293
5319
  if (typeof n === "number" && Number.isFinite(n)) {
@@ -5375,7 +5401,7 @@ function childEnvWithAppSettings(extra) {
5375
5401
  function harnessEnvKey(harness) {
5376
5402
  return HARNESS_ENV_KEYS[harness];
5377
5403
  }
5378
- var import_node_crypto3, import_node_fs15, import_node_os5, import_node_path18, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, FOLLOW_UP_BEHAVIORS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, CONFIRM_ACCOUNT, CONFIRM_PROJECT, DEFAULT_CLI_BIN;
5404
+ var import_node_crypto3, import_node_fs15, import_node_os5, import_node_path18, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, FOLLOW_UP_BEHAVIORS, AGENT_DONE_SOUNDS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, CONFIRM_ACCOUNT, CONFIRM_PROJECT, DEFAULT_CLI_BIN;
5379
5405
  var init_app_settings = __esm({
5380
5406
  "src/store/app-settings.ts"() {
5381
5407
  "use strict";
@@ -5412,6 +5438,18 @@ var init_app_settings = __esm({
5412
5438
  ]);
5413
5439
  GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
5414
5440
  FOLLOW_UP_BEHAVIORS = ["steer", "queue"];
5441
+ AGENT_DONE_SOUNDS = [
5442
+ "none",
5443
+ "goal",
5444
+ "bell",
5445
+ "level",
5446
+ "coin",
5447
+ "fanfare",
5448
+ "magic",
5449
+ "rooster",
5450
+ "whistle",
5451
+ "custom"
5452
+ ];
5415
5453
  CLOUD_CONNECT_AGENTS = /* @__PURE__ */ new Set([
5416
5454
  "claude",
5417
5455
  "codex",
@@ -10242,6 +10280,9 @@ var init_injected_mcp = __esm({
10242
10280
  "mcp__sideboard__present_plan",
10243
10281
  "mcp__sideboard__wait_for_job",
10244
10282
  "mcp__sideboard__stop_job",
10283
+ "mcp__sideboard__list_run_scripts",
10284
+ "mcp__sideboard__run_dev_script",
10285
+ "mcp__sideboard__stop_dev_script",
10245
10286
  "mcp__sideboard__get_viewer_context",
10246
10287
  "mcp__sideboard__update_viewer_context"
10247
10288
  ];
@@ -10451,6 +10492,13 @@ function toolDescription(name, input) {
10451
10492
  if (/stop_job$/i.test(name)) {
10452
10493
  return str2(input?.id) ? `Stop ${str2(input?.id)}` : "Stop job";
10453
10494
  }
10495
+ if (/list_run_scripts$/i.test(name)) return "List run scripts";
10496
+ if (/run_dev_script$/i.test(name)) {
10497
+ return str2(input?.name) ? `Start ${str2(input?.name)}` : "Start Dev script";
10498
+ }
10499
+ if (/stop_dev_script$/i.test(name)) {
10500
+ return str2(input?.name) ? `Stop ${str2(input?.name)}` : "Stop Dev script";
10501
+ }
10454
10502
  if (isSubagentToolName(name)) {
10455
10503
  const desc = str2(input?.description);
10456
10504
  const sub = asRecord(input?.subagentType);
@@ -14630,61 +14678,92 @@ function getRunScript(worktreePath, repoPath, name) {
14630
14678
  function getRunMode(worktreePath, repoPath) {
14631
14679
  return loadWorkspaceSettings(worktreePath, repoPath)?.runMode ?? "concurrent";
14632
14680
  }
14633
- async function allocatePort() {
14681
+ async function reservePort() {
14634
14682
  return new Promise((resolve, reject) => {
14635
14683
  const server = (0, import_node_net.createServer)();
14684
+ let released = false;
14685
+ const release = () => new Promise((res) => {
14686
+ if (released) {
14687
+ res();
14688
+ return;
14689
+ }
14690
+ released = true;
14691
+ server.close(() => res());
14692
+ });
14636
14693
  server.listen(0, "127.0.0.1", () => {
14637
14694
  const addr = server.address();
14638
14695
  if (!addr || typeof addr === "string") {
14639
- server.close();
14640
- reject(new Error("Failed to allocate port"));
14696
+ void release().then(() => reject(new Error("Failed to allocate port")));
14641
14697
  return;
14642
14698
  }
14643
- const port = addr.port;
14644
- server.close((err) => err ? reject(err) : resolve(port));
14699
+ resolve({ port: addr.port, release });
14645
14700
  });
14646
14701
  server.on("error", reject);
14647
14702
  });
14648
14703
  }
14649
- async function allocatePortRange(size = PORT_RANGE_SIZE) {
14650
- const base = await allocatePort();
14651
- const ports = [base];
14652
- for (let i = 1; i < size; i++) {
14653
- const candidate = base + i;
14654
- const free = await new Promise((resolve) => {
14655
- const server = (0, import_node_net.createServer)();
14656
- server.once("error", () => resolve(false));
14657
- server.listen(candidate, "127.0.0.1", () => {
14658
- server.close(() => resolve(true));
14659
- });
14704
+ async function tryReservePort(port) {
14705
+ return new Promise((resolve) => {
14706
+ const server = (0, import_node_net.createServer)();
14707
+ let released = false;
14708
+ const release = () => new Promise((res) => {
14709
+ if (released) {
14710
+ res();
14711
+ return;
14712
+ }
14713
+ released = true;
14714
+ server.close(() => res());
14660
14715
  });
14661
- if (free) {
14662
- ports.push(candidate);
14663
- } else {
14664
- ports.push(await allocatePort());
14716
+ server.once("error", () => resolve(null));
14717
+ server.listen(port, "127.0.0.1", () => {
14718
+ resolve({ port, release });
14719
+ });
14720
+ });
14721
+ }
14722
+ async function reservePortRange(size = PORT_RANGE_SIZE) {
14723
+ const held = [await reservePort()];
14724
+ const base = held[0].port;
14725
+ try {
14726
+ for (let i = 1; i < size; i++) {
14727
+ const candidate = base + i;
14728
+ const next = await tryReservePort(candidate);
14729
+ if (next) {
14730
+ held.push(next);
14731
+ } else {
14732
+ held.push(await reservePort());
14733
+ }
14665
14734
  }
14735
+ return held;
14736
+ } catch (err) {
14737
+ await Promise.all(held.map((h) => h.release()));
14738
+ throw err;
14666
14739
  }
14667
- return ports;
14668
14740
  }
14669
14741
  async function startDevServer(repoPath, worktreePath, onLine, opts) {
14670
14742
  const script = getRunScript(worktreePath, repoPath, opts?.scriptName);
14671
14743
  if (!script) return null;
14672
- const ports = await allocatePortRange(PORT_RANGE_SIZE);
14673
- const handle = await spawnWorkspaceScript(script.command, {
14674
- worktreePath,
14675
- repoPath,
14676
- ports,
14677
- defaultBranch: opts?.defaultBranch,
14678
- onLine
14679
- });
14680
- return {
14681
- pid: handle.pid,
14682
- port: ports[0],
14683
- ports,
14684
- scriptName: script.name,
14685
- kill: handle.kill,
14686
- done: handle.done
14687
- };
14744
+ const held = await reservePortRange(PORT_RANGE_SIZE);
14745
+ const ports = held.map((h) => h.port);
14746
+ try {
14747
+ await Promise.all(held.map((h) => h.release()));
14748
+ const handle = await spawnWorkspaceScript(script.command, {
14749
+ worktreePath,
14750
+ repoPath,
14751
+ ports,
14752
+ defaultBranch: opts?.defaultBranch,
14753
+ onLine
14754
+ });
14755
+ return {
14756
+ pid: handle.pid,
14757
+ port: ports[0],
14758
+ ports,
14759
+ scriptName: script.name,
14760
+ kill: handle.kill,
14761
+ done: handle.done
14762
+ };
14763
+ } catch (err) {
14764
+ await Promise.all(held.map((h) => h.release()));
14765
+ throw err;
14766
+ }
14688
14767
  }
14689
14768
  var import_node_fs37, import_node_child_process4, import_node_net, import_node_path39, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
14690
14769
  var init_conductor = __esm({
@@ -20498,12 +20577,13 @@ function formatLongRunningDirective(opts) {
20498
20577
  "- Loop Sideboard MCP `wait_for_job` (returns in ~45s). The desktop opens a `type=log` pane from wait/stop (and shell detached-job JSON) automatically \u2014 you may still `present_artifact` with `artifact_id=<id>` for a custom title. stillRunning \u2192 wait again. Shell fallback: the same helper with `wait <id>`.",
20499
20578
  "- Hanging, no useful output, or the wrong thing \u2192 `stop_job` (or the helper with `stop <id>`). Do not stop a pack/test/deploy that is clearly making progress.",
20500
20579
  "- Stay in the loop until stillRunning is false (or you stopped it). ok \u2192 finish the task. failed / stopped \u2192 read the log, fix or narrow the command, start once.",
20501
- "State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available)."
20580
+ "State: `.context/.sideboard/detached-jobs/<id>/` (local scratch). Full guide: `/long-running` (always available).",
20581
+ "Project Dev / run scripts (`.sideboard` `[scripts.run.*]`, same as the desktop Dev button): use MCP `list_run_scripts` / `run_dev_script` / `stop_dev_script` \u2014 logs and Stop live in the thread UI. Do not detached-job or shell-spawn that same command."
20502
20582
  ].join("\n");
20503
20583
  }
20504
20584
  function formatLongRunningReminder(opts) {
20505
20585
  const invoke = formatDetachedJobInvoke(opts?.scriptPath);
20506
- return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait_for_job (or detached-job wait). The log pane updates from wait JSON; present_artifact type=log is optional. stop_job if hanging or wrong. Do not say you will let the user know later \u2014 stay in the turn.`;
20586
+ return `Long jobs: \`${invoke} start <id> -- <cmd>\`, loop wait_for_job (or detached-job wait). The log pane updates from wait JSON; present_artifact type=log is optional. stop_job if hanging or wrong. Do not say you will let the user know later \u2014 stay in the turn. Project Dev button: list_run_scripts / run_dev_script / stop_dev_script (UI-connected \u2014 not detached-job).`;
20507
20587
  }
20508
20588
  function formatArtifactDirective() {
20509
20589
  return [
@@ -20515,7 +20595,7 @@ function formatArtifactDirective() {
20515
20595
  ].join("\n");
20516
20596
  }
20517
20597
  function formatUiReminder() {
20518
- return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
20598
+ return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable. Dev / preview: run_dev_script (same as the Dev button); stop_dev_script to stop.";
20519
20599
  }
20520
20600
  var import_node_fs50, import_node_path49, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
20521
20601
  var init_instructions = __esm({
@@ -21662,6 +21742,8 @@ var init_orchestrator = __esm({
21662
21742
  /** Auto-continues after a worktree turn ended while a detached job still runs. */
21663
21743
  jobContinueCount = /* @__PURE__ */ new Map();
21664
21744
  jobContinueNudged = /* @__PURE__ */ new Set();
21745
+ /** In-flight startDev per worktree run key — agent MCP + UI Start must not double-spawn. */
21746
+ startingDev = /* @__PURE__ */ new Map();
21665
21747
  maxConcurrent;
21666
21748
  runningCount = 0;
21667
21749
  constructor(opts) {
@@ -22879,9 +22961,20 @@ var init_orchestrator = __esm({
22879
22961
  "No run script found for this worktree. Add [scripts.run.*] in the worktree\u2019s .sideboard/settings.toml or .conductor/settings.toml (or settings.local.toml on the main repo)."
22880
22962
  );
22881
22963
  }
22964
+ const runKey = worktreeRunProcessKey(thread.worktreePath, resolvedName);
22965
+ const inFlight = this.startingDev.get(runKey);
22966
+ if (inFlight) return inFlight;
22967
+ const start = this.startDevUnlocked(thread, resolvedName, scriptName, scripts, runKey);
22968
+ this.startingDev.set(runKey, start);
22969
+ try {
22970
+ return await start;
22971
+ } finally {
22972
+ this.startingDev.delete(runKey);
22973
+ }
22974
+ }
22975
+ async startDevUnlocked(thread, resolvedName, scriptName, scripts, runKey) {
22882
22976
  const siblings = threadsSharingWorktree(thread.worktreePath);
22883
22977
  const shared = mergeWorktreeActiveRuns(siblings);
22884
- const runKey = worktreeRunProcessKey(thread.worktreePath, resolvedName);
22885
22978
  const existing = this.processes.get(runKey) ?? siblings.map((t) => this.processes.get(`${t.id}:run:${resolvedName}`)).find(Boolean);
22886
22979
  const active = shared.activeRuns.find((r) => r.scriptName === resolvedName);
22887
22980
  if (existing && active) {
@@ -22893,6 +22986,14 @@ var init_orchestrator = __esm({
22893
22986
  return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
22894
22987
  }
22895
22988
  }
22989
+ if (active && !existing) {
22990
+ killListenersOnPorts(collectActiveRunPorts([active]));
22991
+ this.syncWorktreeRuns(
22992
+ thread.worktreePath,
22993
+ shared.activeRuns.filter((r) => r.scriptName !== resolvedName),
22994
+ shared.devPort === active.port ? null : shared.devPort
22995
+ );
22996
+ }
22896
22997
  const mode = getRunMode(thread.worktreePath, thread.repoPath);
22897
22998
  if (mode === "nonconcurrent") {
22898
22999
  const here = normalizeWorktreePath(thread.worktreePath);
@@ -22941,6 +23042,7 @@ var init_orchestrator = __esm({
22941
23042
  kill: handle.kill
22942
23043
  });
22943
23044
  }
23045
+ const latestShared = mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath));
22944
23046
  const run2 = {
22945
23047
  scriptName: resolvedName,
22946
23048
  port: handle.port,
@@ -22948,13 +23050,13 @@ var init_orchestrator = __esm({
22948
23050
  startedAt
22949
23051
  };
22950
23052
  const nextRuns = [
22951
- ...shared.activeRuns.filter((r) => r.scriptName !== resolvedName),
23053
+ ...latestShared.activeRuns.filter((r) => r.scriptName !== resolvedName),
22952
23054
  run2
22953
23055
  ];
22954
23056
  this.syncWorktreeRuns(
22955
23057
  thread.worktreePath,
22956
23058
  nextRuns,
22957
- isDefault ? handle.port : shared.devPort
23059
+ isDefault ? handle.port : latestShared.devPort
22958
23060
  );
22959
23061
  this.emit({
22960
23062
  type: "dev_server_started",
@@ -25677,6 +25779,23 @@ function mcpArchiveBlockedReason(thread) {
25677
25779
 
25678
25780
  // src/mcp/server.ts
25679
25781
  init_profile();
25782
+
25783
+ // src/mcp/run-script-ref.ts
25784
+ init_chat_tabs();
25785
+ function resolveRunScriptThreadRef(ref) {
25786
+ const explicit = ref?.trim();
25787
+ if (explicit) return explicit;
25788
+ const siblings = threadsSharingWorktree(process.cwd());
25789
+ const thread = siblings[0];
25790
+ if (!thread) {
25791
+ throw new Error(
25792
+ "No Sideboard thread for this cwd. Pass ref (thread id), or call from a worktree chat."
25793
+ );
25794
+ }
25795
+ return thread.id;
25796
+ }
25797
+
25798
+ // src/mcp/server.ts
25680
25799
  init_wait_for_turn();
25681
25800
  init_wait_for_job();
25682
25801
  init_message_parts();
@@ -28108,13 +28227,73 @@ async function startMcpServer() {
28108
28227
  "Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. The type=log pane updates to status=failed with the last delta. Then decide the next step.",
28109
28228
  {
28110
28229
  id: import_zod9.z.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
28111
- reason: import_zod9.z.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the error)")
28230
+ reason: import_zod9.z.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
28112
28231
  },
28113
28232
  async ({ id, reason }) => {
28114
28233
  const result = await stopDetachedJob(process.cwd(), id, { reason });
28115
28234
  return mcpJson(result);
28116
28235
  }
28117
28236
  );
28237
+ server.tool(
28238
+ "list_run_scripts",
28239
+ "List .sideboard/.conductor run scripts for this thread (same menu as the desktop Dev button) and which are active. Worktree turns may omit ref (uses cwd).",
28240
+ {
28241
+ ref: import_zod9.z.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd).")
28242
+ },
28243
+ async ({ ref }) => {
28244
+ try {
28245
+ const threadRef = resolveRunScriptThreadRef(ref);
28246
+ const scripts = orch.listThreadRunScripts(threadRef);
28247
+ const active = orch.getActiveRuns(threadRef);
28248
+ return mcpJson({ scripts, active, ref: threadRef });
28249
+ } catch (err) {
28250
+ const message = err instanceof Error ? err.message : String(err);
28251
+ return { content: [{ type: "text", text: message }], isError: true };
28252
+ }
28253
+ }
28254
+ );
28255
+ server.tool(
28256
+ "run_dev_script",
28257
+ "Start a .sideboard/.conductor run script (same as the desktop Dev / play button). Logs and Stop appear in the thread UI \u2014 do not detached-job or shell-spawn the same command. Returns the allocated port. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
28258
+ {
28259
+ ref: import_zod9.z.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
28260
+ name: import_zod9.z.string().optional().describe("Script name from list_run_scripts (default script if omitted).")
28261
+ },
28262
+ async ({ ref, name }) => {
28263
+ try {
28264
+ const threadRef = resolveRunScriptThreadRef(ref);
28265
+ const result = await orch.startDev(threadRef, name);
28266
+ return mcpJson({
28267
+ port: result.port,
28268
+ scriptName: result.scriptName,
28269
+ ports: result.ports,
28270
+ url: `http://localhost:${result.port}`,
28271
+ ref: threadRef
28272
+ });
28273
+ } catch (err) {
28274
+ const message = err instanceof Error ? err.message : String(err);
28275
+ return { content: [{ type: "text", text: message }], isError: true };
28276
+ }
28277
+ }
28278
+ );
28279
+ server.tool(
28280
+ "stop_dev_script",
28281
+ "Stop a run script started with run_dev_script or the desktop Dev button (same as UI Stop). Omit name to stop all scripts on this worktree. Worktree turns may omit ref (uses cwd).",
28282
+ {
28283
+ ref: import_zod9.z.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
28284
+ name: import_zod9.z.string().optional().describe("Script name to stop; omit to stop all active scripts.")
28285
+ },
28286
+ async ({ ref, name }) => {
28287
+ try {
28288
+ const threadRef = resolveRunScriptThreadRef(ref);
28289
+ orch.stopDev(threadRef, name);
28290
+ return mcpJson({ ok: true, ref: threadRef, stopped: name ?? "all" });
28291
+ } catch (err) {
28292
+ const message = err instanceof Error ? err.message : String(err);
28293
+ return { content: [{ type: "text", text: message }], isError: true };
28294
+ }
28295
+ }
28296
+ );
28118
28297
  if (!worktreeProfile) {
28119
28298
  registerSlackTools(server);
28120
28299
  registerConnectedIssueVendorTools(server);
@@ -28593,45 +28772,6 @@ async function startMcpServer() {
28593
28772
  }
28594
28773
  }
28595
28774
  );
28596
- server.tool(
28597
- "run_dev_script",
28598
- "Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
28599
- {
28600
- ref: import_zod9.z.string(),
28601
- name: import_zod9.z.string().optional()
28602
- },
28603
- async ({ ref, name }) => {
28604
- const result = await orch.startDev(ref, name);
28605
- return mcpJson({
28606
- port: result.port,
28607
- scriptName: result.scriptName,
28608
- ports: result.ports,
28609
- url: `http://localhost:${result.port}`
28610
- });
28611
- }
28612
- );
28613
- server.tool(
28614
- "list_run_scripts",
28615
- "List named run scripts available for a thread",
28616
- { ref: import_zod9.z.string() },
28617
- async ({ ref }) => {
28618
- const scripts = orch.listThreadRunScripts(ref);
28619
- const active = orch.getActiveRuns(ref);
28620
- return mcpJson({ scripts, active });
28621
- }
28622
- );
28623
- server.tool(
28624
- "stop_dev_script",
28625
- "Stop a running script for a thread (all scripts if name omitted)",
28626
- {
28627
- ref: import_zod9.z.string(),
28628
- name: import_zod9.z.string().optional()
28629
- },
28630
- async ({ ref, name }) => {
28631
- orch.stopDev(ref, name);
28632
- return { content: [{ type: "text", text: "ok" }] };
28633
- }
28634
- );
28635
28775
  server.tool(
28636
28776
  "run_setup",
28637
28777
  "Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",